mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 18:09:56 +00:00
media/bcm2835-unicam: Parse pad numbers correctly
The driver was making big assumptions about the source device using pad 0 and 1, which doesn't follow for more complex devices where Unicam's source device may be a sink device for something else. Read the pad numbers through media controller, and reference them appropriately. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
This commit is contained in:
committed by
Dom Cobley
parent
9552cceb2c
commit
a8d4489ec1
@@ -372,6 +372,8 @@ struct unicam_node {
|
|||||||
int open;
|
int open;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
unsigned int pad_id;
|
unsigned int pad_id;
|
||||||
|
/* Source pad id on the sensor for this node */
|
||||||
|
unsigned int src_pad_id;
|
||||||
/* Pointer pointing to current v4l2_buffer */
|
/* Pointer pointing to current v4l2_buffer */
|
||||||
struct unicam_buffer *cur_frm;
|
struct unicam_buffer *cur_frm;
|
||||||
/* Pointer pointing to next v4l2_buffer */
|
/* Pointer pointing to next v4l2_buffer */
|
||||||
@@ -580,7 +582,7 @@ static int __subdev_get_format(struct unicam_device *dev,
|
|||||||
{
|
{
|
||||||
struct v4l2_subdev_format sd_fmt = {
|
struct v4l2_subdev_format sd_fmt = {
|
||||||
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
||||||
.pad = pad_id
|
.pad = dev->node[pad_id].src_pad_id,
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -602,7 +604,7 @@ static int __subdev_set_format(struct unicam_device *dev,
|
|||||||
{
|
{
|
||||||
struct v4l2_subdev_format sd_fmt = {
|
struct v4l2_subdev_format sd_fmt = {
|
||||||
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
||||||
.pad = pad_id
|
.pad = dev->node[pad_id].src_pad_id,
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -1970,7 +1972,7 @@ static int unicam_enum_framesizes(struct file *file, void *priv,
|
|||||||
|
|
||||||
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||||
fse.index = fsize->index;
|
fse.index = fsize->index;
|
||||||
fse.pad = node->pad_id;
|
fse.pad = node->src_pad_id;
|
||||||
|
|
||||||
ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse);
|
ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -1995,6 +1997,7 @@ static int unicam_enum_frameintervals(struct file *file, void *priv,
|
|||||||
const struct unicam_fmt *fmt;
|
const struct unicam_fmt *fmt;
|
||||||
struct v4l2_subdev_frame_interval_enum fie = {
|
struct v4l2_subdev_frame_interval_enum fie = {
|
||||||
.index = fival->index,
|
.index = fival->index,
|
||||||
|
.pad = node->src_pad_id,
|
||||||
.width = fival->width,
|
.width = fival->width,
|
||||||
.height = fival->height,
|
.height = fival->height,
|
||||||
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
||||||
@@ -2086,8 +2089,13 @@ static int unicam_enum_dv_timings(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct unicam_node *node = video_drvdata(file);
|
struct unicam_node *node = video_drvdata(file);
|
||||||
struct unicam_device *dev = node->dev;
|
struct unicam_device *dev = node->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
|
timings->pad = node->src_pad_id;
|
||||||
|
ret = v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
|
||||||
|
timings->pad = node->pad_id;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unicam_dv_timings_cap(struct file *file, void *priv,
|
static int unicam_dv_timings_cap(struct file *file, void *priv,
|
||||||
@@ -2095,8 +2103,13 @@ static int unicam_dv_timings_cap(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct unicam_node *node = video_drvdata(file);
|
struct unicam_node *node = video_drvdata(file);
|
||||||
struct unicam_device *dev = node->dev;
|
struct unicam_device *dev = node->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
|
cap->pad = node->src_pad_id;
|
||||||
|
ret = v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
|
||||||
|
cap->pad = node->pad_id;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unicam_subscribe_event(struct v4l2_fh *fh,
|
static int unicam_subscribe_event(struct v4l2_fh *fh,
|
||||||
@@ -2367,14 +2380,12 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node,
|
|||||||
*/
|
*/
|
||||||
fmt = get_first_supported_format(unicam);
|
fmt = get_first_supported_format(unicam);
|
||||||
|
|
||||||
if (!fmt)
|
if (fmt) {
|
||||||
/* No compatible formats */
|
mbus_fmt.code = fmt->code;
|
||||||
return -EINVAL;
|
ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
|
||||||
|
if (ret)
|
||||||
mbus_fmt.code = fmt->code;
|
return -EINVAL;
|
||||||
ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
|
}
|
||||||
if (ret)
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
if (mbus_fmt.field != V4L2_FIELD_NONE) {
|
if (mbus_fmt.field != V4L2_FIELD_NONE) {
|
||||||
/* Interlaced not supported - disable it now. */
|
/* Interlaced not supported - disable it now. */
|
||||||
@@ -2384,7 +2395,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc
|
if (fmt)
|
||||||
|
node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc
|
||||||
: fmt->repacked_fourcc;
|
: fmt->repacked_fourcc;
|
||||||
} else {
|
} else {
|
||||||
/* Fix this node format as embedded data. */
|
/* Fix this node format as embedded data. */
|
||||||
@@ -2397,7 +2409,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node,
|
|||||||
node->fmt = fmt;
|
node->fmt = fmt;
|
||||||
|
|
||||||
/* Read current subdev format */
|
/* Read current subdev format */
|
||||||
unicam_reset_format(node);
|
if (fmt)
|
||||||
|
unicam_reset_format(node);
|
||||||
|
|
||||||
if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
|
if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
|
||||||
v4l2_std_id tvnorms;
|
v4l2_std_id tvnorms;
|
||||||
@@ -2486,6 +2499,7 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node,
|
|||||||
unicam_err(unicam, "Unable to allocate dummy buffer.\n");
|
unicam_err(unicam, "Unable to allocate dummy buffer.\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pad_id == METADATA_PAD ||
|
if (pad_id == METADATA_PAD ||
|
||||||
!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
|
!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
|
||||||
v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
|
v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
|
||||||
@@ -2544,7 +2558,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node,
|
|||||||
node->registered = true;
|
node->registered = true;
|
||||||
|
|
||||||
if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) {
|
if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) {
|
||||||
ret = media_create_pad_link(&unicam->sensor->entity, pad_id,
|
ret = media_create_pad_link(&unicam->sensor->entity,
|
||||||
|
node->src_pad_id,
|
||||||
&node->video_dev.entity, 0,
|
&node->video_dev.entity, 0,
|
||||||
MEDIA_LNK_FL_ENABLED |
|
MEDIA_LNK_FL_ENABLED |
|
||||||
MEDIA_LNK_FL_IMMUTABLE);
|
MEDIA_LNK_FL_IMMUTABLE);
|
||||||
@@ -2576,9 +2591,11 @@ static void unregister_nodes(struct unicam_device *unicam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unicam_probe_complete(struct unicam_device *unicam)
|
static int unicam_async_complete(struct v4l2_async_notifier *notifier)
|
||||||
{
|
{
|
||||||
static struct lock_class_key key;
|
static struct lock_class_key key;
|
||||||
|
struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev);
|
||||||
|
unsigned int i, source_pads = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
unicam->v4l2_dev.notify = unicam_notify;
|
unicam->v4l2_dev.notify = unicam_notify;
|
||||||
@@ -2588,7 +2605,20 @@ static int unicam_probe_complete(struct unicam_device *unicam)
|
|||||||
if (!unicam->sensor_state)
|
if (!unicam->sensor_state)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2);
|
for (i = 0; i < unicam->sensor->entity.num_pads; i++) {
|
||||||
|
if (unicam->sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) {
|
||||||
|
if (source_pads < MAX_NODES) {
|
||||||
|
unicam->node[source_pads].src_pad_id = i;
|
||||||
|
unicam_err(unicam, "source pad %u is index %u\n",
|
||||||
|
source_pads, i);
|
||||||
|
}
|
||||||
|
source_pads++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!source_pads) {
|
||||||
|
unicam_err(unicam, "No source pads on sensor.\n");
|
||||||
|
goto unregister;
|
||||||
|
}
|
||||||
|
|
||||||
ret = register_node(unicam, &unicam->node[IMAGE_PAD],
|
ret = register_node(unicam, &unicam->node[IMAGE_PAD],
|
||||||
V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD);
|
V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD);
|
||||||
@@ -2597,11 +2627,15 @@ static int unicam_probe_complete(struct unicam_device *unicam)
|
|||||||
goto unregister;
|
goto unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = register_node(unicam, &unicam->node[METADATA_PAD],
|
if (source_pads >= 2) {
|
||||||
V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD);
|
unicam->sensor_embedded_data = true;
|
||||||
if (ret) {
|
|
||||||
unicam_err(unicam, "Unable to register metadata video device.\n");
|
ret = register_node(unicam, &unicam->node[METADATA_PAD],
|
||||||
goto unregister;
|
V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD);
|
||||||
|
if (ret) {
|
||||||
|
unicam_err(unicam, "Unable to register metadata video device.\n");
|
||||||
|
goto unregister;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
|
ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
|
||||||
@@ -2624,13 +2658,6 @@ unregister:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unicam_async_complete(struct v4l2_async_notifier *notifier)
|
|
||||||
{
|
|
||||||
struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev);
|
|
||||||
|
|
||||||
return unicam_probe_complete(unicam);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct v4l2_async_notifier_operations unicam_async_ops = {
|
static const struct v4l2_async_notifier_operations unicam_async_ops = {
|
||||||
.bound = unicam_async_bound,
|
.bound = unicam_async_bound,
|
||||||
.complete = unicam_async_complete,
|
.complete = unicam_async_complete,
|
||||||
@@ -2739,7 +2766,7 @@ static int of_unicam_connect_subdevs(struct unicam_device *dev)
|
|||||||
dev->notifier.ops = &unicam_async_ops;
|
dev->notifier.ops = &unicam_async_ops;
|
||||||
|
|
||||||
dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
|
dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
|
||||||
dev->asd.match.fwnode = of_fwnode_handle(sensor_node);
|
dev->asd.match.fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep_node));
|
||||||
ret = __v4l2_async_nf_add_subdev(&dev->notifier, &dev->asd);
|
ret = __v4l2_async_nf_add_subdev(&dev->notifier, &dev->asd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
unicam_err(dev, "Error adding subdevice: %d\n", ret);
|
unicam_err(dev, "Error adding subdevice: %d\n", ret);
|
||||||
|
|||||||
Reference in New Issue
Block a user