Загрузка данных
static inline struct mxc_isi_crossbar *to_isi_crossbar(struct v4l2_subdev *sd)
{
return container_of(sd, struct mxc_isi_crossbar, sd);
}
static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar,
struct v4l2_subdev_state *state,
struct v4l2_subdev *remote_sd,
u32 remote_pad, unsigned int port)
{
struct mxc_isi_dev *isi = xbar->isi;
const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;
const struct v4l2_mbus_framefmt *fmt;
struct v4l2_mbus_frame_desc fd;
unsigned int stream;
int ret;
if (!gasket_ops)
return 0;
/*
* Configure and enable the gasket with the frame size and CSI-2 data
* type. For YUV422 8-bit, enable dual component mode unconditionally,
* to match the configuration of the CSIS.
*/
ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, remote_pad, &fd);
if (ret) {
dev_err(isi->dev,
"failed to get frame descriptor from '%s':%u: %d\n",
remote_sd->name, remote_pad, ret);
return ret;
}
/*
* For single or multiple stream, only the first stream be used
* since gasket enable callback be called only once.
*/
stream = fd.num_entries > 0 ? fd.entry[0].stream : 0;
fmt = v4l2_subdev_state_get_format(state, port, stream);
if (!fmt)
return -EINVAL;
gasket_ops->enable(isi, &fd, fmt, port);
return 0;
}
static void mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar *xbar,
unsigned int port)
{
struct mxc_isi_dev *isi = xbar->isi;
const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;
if (!gasket_ops)
return;
gasket_ops->disable(isi, port);
}
/* -----------------------------------------------------------------------------
* V4L2 subdev operations
*/
static const struct v4l2_mbus_framefmt mxc_isi_crossbar_default_format = {
.code = MXC_ISI_DEF_MBUS_CODE_SINK,
.width = MXC_ISI_DEF_WIDTH,
.height = MXC_ISI_DEF_HEIGHT,
.field = V4L2_FIELD_NONE,
.colorspace = MXC_ISI_DEF_COLOR_SPACE,
.ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC,
.quantization = MXC_ISI_DEF_QUANTIZATION,
.xfer_func = MXC_ISI_DEF_XFER_FUNC,
};
static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_krouting *routing)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
struct v4l2_subdev_route *route;
int ret;
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_NO_N_TO_1);
if (ret)
return ret;
/* The memory input can be routed to the first pipeline only. */
for_each_active_route(&state->routing, route) {
if (route->sink_pad == xbar->num_sinks - 1 &&
route->source_pad != xbar->num_sinks) {
dev_dbg(xbar->isi->dev,
"invalid route from memory input (%u) to pipe %u\n",
route->sink_pad,
route->source_pad - xbar->num_sinks);
return -EINVAL;
}
}
return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
&mxc_isi_crossbar_default_format);
}
static struct v4l2_subdev *
mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar,
struct v4l2_subdev_state *state,
u32 source_pad, u64 source_streams,
u32 *__sink_pad, u64 *__sink_streams,
u32 *remote_pad)
{
struct v4l2_subdev_route *route;
struct v4l2_subdev *sd;
struct media_pad *pad;
u64 sink_streams = 0;
int sink_pad = -1;
/*
* Translate the source pad and streams to the sink side. The routing
* validation forbids stream merging, so all matching entries in the
* routing table are guaranteed to have the same sink pad.
*
* TODO: This is likely worth a helper function, it could perhaps be
* supported by v4l2_subdev_state_xlate_streams() with pad1 set to -1.
*/
for_each_active_route(&state->routing, route) {
if (route->source_pad != source_pad ||
!(source_streams & BIT(route->source_stream)))
continue;
sink_streams |= BIT(route->sink_stream);
sink_pad = route->sink_pad;
}
if (sink_pad < 0) {
dev_dbg(xbar->isi->dev,
"no stream connected to pipeline %u\n",
source_pad - xbar->num_sinks);
return ERR_PTR(-EPIPE);
}
pad = media_pad_remote_pad_first(&xbar->pads[sink_pad]);
if (!pad) {
dev_err(xbar->isi->dev,
"no remote pad found for sink pad %u\n",
sink_pad);
return ERR_PTR(-EPIPE);
}
sd = media_entity_to_v4l2_subdev(pad->entity);
if (!sd) {
dev_dbg(xbar->isi->dev,
"no entity connected to crossbar input %u\n",
sink_pad);
return ERR_PTR(-EPIPE);
}
*__sink_pad = sink_pad;
*__sink_streams = sink_streams;
*remote_pad = pad->index;
return sd;
}
static int mxc_isi_create_default_routing(struct mxc_isi_crossbar *xbar,
struct v4l2_subdev_route *routes)
{
struct device *dev = xbar->isi->dev;
struct device_node *node;
unsigned int index = 0;
int i, j;
for_each_endpoint_of_node(dev->of_node, node) {
struct of_endpoint ep;
of_graph_parse_endpoint(node, &ep);
if (ep.port > xbar->isi->pdata->num_ports) {
dev_err(dev, "Invalid port number(%d)\n", ep.port);
return -EINVAL;
}
xbar->inputs[ep.port].connected = true;
}
for (i = 0; i < xbar->num_sources; ++i) {
struct v4l2_subdev_route *route = &routes[i];
j = index;
while (j < xbar->num_sinks) {
if (!xbar->inputs[j].connected) {
j = (++index) % xbar->num_sinks;
continue;
}
route->sink_pad = j;
route->source_pad = i + xbar->num_sinks;
route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
index = (j + 1) % xbar->num_sinks;
dev_dbg(dev, "route: sink(%d) -> source(%d)\n",
route->sink_pad, route->source_pad);
break;
}
}
return 0;
}
static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
struct v4l2_subdev_krouting routing = { };
struct v4l2_subdev_route *routes;
int ret;
/*
* Create a 1:N mapping between pixel link inputs and outputs to
* pipelines by according to the number of pixel links inputs and
* ISI channels. The algorithm will divide the ISI channel equally
* to each pixel link input which connect to a remote device.
*/
routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL);
if (!routes)
return -ENOMEM;
ret = mxc_isi_create_default_routing(xbar, routes);
if (ret < 0) {
kfree(routes);
return ret;
}
routing.num_routes = xbar->num_sources;
routing.routes = routes;
ret = __mxc_isi_crossbar_set_routing(sd, state, &routing);
kfree(routes);
return ret;
}