From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 03 Jan 2024 09:43:39 +0000
Subject: [PATCH] update kernel to 5.10.198
---
kernel/drivers/media/platform/stm32/stm32-dcmi.c | 407 +++++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 316 insertions(+), 91 deletions(-)
diff --git a/kernel/drivers/media/platform/stm32/stm32-dcmi.c b/kernel/drivers/media/platform/stm32/stm32-dcmi.c
index ee1a211..233e4d3 100644
--- a/kernel/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/kernel/drivers/media/platform/stm32/stm32-dcmi.c
@@ -97,11 +97,13 @@
#define TIMEOUT_MS 1000
-struct dcmi_graph_entity {
- struct device_node *node;
+#define OVERRUN_ERROR_THRESHOLD 3
+struct dcmi_graph_entity {
struct v4l2_async_subdev asd;
- struct v4l2_subdev *subdev;
+
+ struct device_node *remote_node;
+ struct v4l2_subdev *source;
};
struct dcmi_format {
@@ -133,6 +135,7 @@
int sequence;
struct list_head buffers;
struct dcmi_buf *active;
+ int irq;
struct v4l2_device v4l2_dev;
struct video_device *vdev;
@@ -167,6 +170,10 @@
/* Ensure DMA operations atomicity */
struct mutex dma_lock;
+
+ struct media_device mdev;
+ struct media_pad vid_cap_pad;
+ struct media_pipeline pipeline;
};
static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
@@ -446,11 +453,13 @@
spin_lock_irq(&dcmi->irqlock);
- if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
- dcmi->errors_count++;
- if (dcmi->misr & IT_OVR)
- dcmi->overrun_count++;
+ if (dcmi->misr & IT_OVR) {
+ dcmi->overrun_count++;
+ if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD)
+ dcmi->errors_count++;
}
+ if (dcmi->misr & IT_ERR)
+ dcmi->errors_count++;
if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG &&
dcmi->misr & IT_FRAME) {
@@ -576,6 +585,144 @@
spin_unlock_irq(&dcmi->irqlock);
}
+static struct media_entity *dcmi_find_source(struct stm32_dcmi *dcmi)
+{
+ struct media_entity *entity = &dcmi->vdev->entity;
+ struct media_pad *pad;
+
+ /* Walk searching for entity having no sink */
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ }
+
+ return entity;
+}
+
+static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi,
+ struct v4l2_subdev_pad_config *pad_cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct media_entity *entity = &dcmi->entity.source->entity;
+ struct v4l2_subdev *subdev;
+ struct media_pad *sink_pad = NULL;
+ struct media_pad *src_pad = NULL;
+ struct media_pad *pad = NULL;
+ struct v4l2_subdev_format fmt = *format;
+ bool found = false;
+ int ret;
+
+ /*
+ * Starting from sensor subdevice, walk within
+ * pipeline and set format on each subdevice
+ */
+ while (1) {
+ unsigned int i;
+
+ /* Search if current entity has a source pad */
+ for (i = 0; i < entity->num_pads; i++) {
+ pad = &entity->pads[i];
+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
+ src_pad = pad;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ /* Propagate format on sink pad if any, otherwise source pad */
+ if (sink_pad)
+ pad = sink_pad;
+
+ dev_dbg(dcmi->dev, "\"%s\":%d pad format set to 0x%x %ux%u\n",
+ subdev->name, pad->index, format->format.code,
+ format->format.width, format->format.height);
+
+ fmt.pad = pad->index;
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n",
+ __func__, format->format.code,
+ format->format.width, format->format.height,
+ subdev->name, pad->index, ret);
+ return ret;
+ }
+
+ if (fmt.format.code != format->format.code ||
+ fmt.format.width != format->format.width ||
+ fmt.format.height != format->format.height) {
+ dev_dbg(dcmi->dev, "\"%s\":%d pad format has been changed to 0x%x %ux%u\n",
+ subdev->name, pad->index, fmt.format.code,
+ fmt.format.width, fmt.format.height);
+ }
+
+ /* Walk to next entity */
+ sink_pad = media_entity_remote_pad(src_pad);
+ if (!sink_pad || !is_media_entity_v4l2_subdev(sink_pad->entity))
+ break;
+
+ entity = sink_pad->entity;
+ }
+ *format = fmt;
+
+ return 0;
+}
+
+static int dcmi_pipeline_s_stream(struct stm32_dcmi *dcmi, int state)
+{
+ struct media_entity *entity = &dcmi->vdev->entity;
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ int ret;
+
+ /* Start/stop all entities within pipeline */
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, state);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(dcmi->dev, "%s: \"%s\" failed to %s streaming (%d)\n",
+ __func__, subdev->name,
+ state ? "start" : "stop", ret);
+ return ret;
+ }
+
+ dev_dbg(dcmi->dev, "\"%s\" is %s\n",
+ subdev->name, state ? "started" : "stopped");
+ }
+
+ return 0;
+}
+
+static int dcmi_pipeline_start(struct stm32_dcmi *dcmi)
+{
+ return dcmi_pipeline_s_stream(dcmi, 1);
+}
+
+static void dcmi_pipeline_stop(struct stm32_dcmi *dcmi)
+{
+ dcmi_pipeline_s_stream(dcmi, 0);
+}
+
static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
@@ -590,13 +737,16 @@
goto err_pm_put;
}
- /* Enable stream on the sub device */
- ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD) {
- dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error",
- __func__);
+ ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
+ __func__, ret);
goto err_pm_put;
}
+
+ ret = dcmi_pipeline_start(dcmi);
+ if (ret)
+ goto err_media_pipeline_stop;
spin_lock_irq(&dcmi->irqlock);
@@ -669,16 +819,22 @@
if (ret) {
dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n",
__func__);
- goto err_subdev_streamoff;
+ goto err_pipeline_stop;
}
/* Enable interruptions */
- reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
+ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+ else
+ reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR);
return 0;
-err_subdev_streamoff:
- v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
+err_pipeline_stop:
+ dcmi_pipeline_stop(dcmi);
+
+err_media_pipeline_stop:
+ media_pipeline_stop(&dcmi->vdev->entity);
err_pm_put:
pm_runtime_put(dcmi->dev);
@@ -701,13 +857,10 @@
{
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
struct dcmi_buf *buf, *node;
- int ret;
- /* Disable stream on the sub device */
- ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
- if (ret && ret != -ENOIOCTLCMD)
- dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n",
- __func__, ret);
+ dcmi_pipeline_stop(dcmi);
+
+ media_pipeline_stop(&dcmi->vdev->entity);
spin_lock_irq(&dcmi->irqlock);
@@ -848,7 +1001,7 @@
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
+ ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt,
&pad_cfg, &format);
if (ret < 0)
return ret;
@@ -925,8 +1078,7 @@
mf->width = sd_framesize.width;
mf->height = sd_framesize.height;
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
- set_fmt, NULL, &format);
+ ret = dcmi_pipeline_s_fmt(dcmi, NULL, &format);
if (ret < 0)
return ret;
@@ -982,7 +1134,7 @@
};
int ret;
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_fmt, NULL, &fmt);
+ ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt);
if (ret)
return ret;
@@ -1011,7 +1163,7 @@
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
+ ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt,
&pad_cfg, &format);
if (ret < 0)
return ret;
@@ -1034,7 +1186,7 @@
/*
* Get sensor bounds first
*/
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_selection,
+ ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection,
NULL, &bounds);
if (!ret)
*r = bounds.r;
@@ -1167,10 +1319,10 @@
static int dcmi_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver));
- strlcpy(cap->card, "STM32 Camera Memory Interface",
+ strscpy(cap->driver, DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "STM32 Camera Memory Interface",
sizeof(cap->card));
- strlcpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info));
+ strscpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info));
return 0;
}
@@ -1181,7 +1333,7 @@
return -EINVAL;
i->type = V4L2_INPUT_TYPE_CAMERA;
- strlcpy(i->name, "Camera", sizeof(i->name));
+ strscpy(i->name, "Camera", sizeof(i->name));
return 0;
}
@@ -1215,7 +1367,7 @@
fse.code = sd_fmt->mbus_code;
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad, enum_frame_size,
+ ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size,
NULL, &fse);
if (ret)
return ret;
@@ -1232,7 +1384,7 @@
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+ return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p);
}
static int dcmi_s_parm(struct file *file, void *priv,
@@ -1240,7 +1392,7 @@
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+ return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p);
}
static int dcmi_enum_frameintervals(struct file *file, void *fh,
@@ -1262,7 +1414,7 @@
fie.code = sd_fmt->mbus_code;
- ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
+ ret = v4l2_subdev_call(dcmi->entity.source, pad,
enum_frame_interval, NULL, &fie);
if (ret)
return ret;
@@ -1282,7 +1434,7 @@
static int dcmi_open(struct file *file)
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- struct v4l2_subdev *sd = dcmi->entity.subdev;
+ struct v4l2_subdev *sd = dcmi->entity.source;
int ret;
if (mutex_lock_interruptible(&dcmi->lock))
@@ -1313,7 +1465,7 @@
static int dcmi_release(struct file *file)
{
struct stm32_dcmi *dcmi = video_drvdata(file);
- struct v4l2_subdev *sd = dcmi->entity.subdev;
+ struct v4l2_subdev *sd = dcmi->entity.source;
bool fh_singular;
int ret;
@@ -1400,6 +1552,12 @@
return 0;
}
+/*
+ * FIXME: For the time being we only support subdevices
+ * which expose RGB & YUV "parallel form" mbus code (_2X8).
+ * Nevertheless, this allows to support serial source subdevices
+ * and serial to parallel bridges which conform to this.
+ */
static const struct dcmi_format dcmi_formats[] = {
{
.fourcc = V4L2_PIX_FMT_RGB565,
@@ -1424,7 +1582,7 @@
{
const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)];
unsigned int num_fmts = 0, i, j;
- struct v4l2_subdev *subdev = dcmi->entity.subdev;
+ struct v4l2_subdev *subdev = dcmi->entity.source;
struct v4l2_subdev_mbus_code_enum mbus_code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
@@ -1438,12 +1596,20 @@
/* Code supported, have we got this fourcc yet? */
for (j = 0; j < num_fmts; j++)
if (sd_fmts[j]->fourcc ==
- dcmi_formats[i].fourcc)
+ dcmi_formats[i].fourcc) {
/* Already available */
+ dev_dbg(dcmi->dev, "Skipping fourcc/code: %4.4s/0x%x\n",
+ (char *)&sd_fmts[j]->fourcc,
+ mbus_code.code);
break;
- if (j == num_fmts)
+ }
+ if (j == num_fmts) {
/* New */
sd_fmts[num_fmts++] = dcmi_formats + i;
+ dev_dbg(dcmi->dev, "Supported fourcc/code: %4.4s/0x%x\n",
+ (char *)&sd_fmts[num_fmts - 1]->fourcc,
+ sd_fmts[num_fmts - 1]->mbus_code);
+ }
}
mbus_code.index++;
}
@@ -1470,7 +1636,7 @@
static int dcmi_framesizes_init(struct stm32_dcmi *dcmi)
{
unsigned int num_fsize = 0;
- struct v4l2_subdev *subdev = dcmi->entity.subdev;
+ struct v4l2_subdev *subdev = dcmi->entity.source;
struct v4l2_subdev_frame_size_enum fse = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.code = dcmi->sd_format->mbus_code,
@@ -1517,7 +1683,20 @@
struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
int ret;
- dcmi->vdev->ctrl_handler = dcmi->entity.subdev->ctrl_handler;
+ /*
+ * Now that the graph is complete,
+ * we search for the source subdevice
+ * in order to expose it through V4L2 interface
+ */
+ dcmi->entity.source =
+ media_entity_to_v4l2_subdev(dcmi_find_source(dcmi));
+ if (!dcmi->entity.source) {
+ dev_err(dcmi->dev, "Source subdevice not found\n");
+ return -ENODEV;
+ }
+
+ dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler;
+
ret = dcmi_formats_init(dcmi);
if (ret) {
dev_err(dcmi->dev, "No supported mediabus format found\n");
@@ -1542,14 +1721,14 @@
return ret;
}
- ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
+ ret = devm_request_threaded_irq(dcmi->dev, dcmi->irq, dcmi_irq_callback,
+ dcmi_irq_thread, IRQF_ONESHOT,
+ dev_name(dcmi->dev), dcmi);
if (ret) {
- dev_err(dcmi->dev, "Failed to register video device\n");
+ dev_err(dcmi->dev, "Unable to request irq %d\n", dcmi->irq);
return ret;
}
- dev_dbg(dcmi->dev, "Device registered as %s\n",
- video_device_node_name(dcmi->vdev));
return 0;
}
@@ -1561,7 +1740,7 @@
dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev));
- /* Checks internaly if vdev has been init or not */
+ /* Checks internally if vdev has been init or not */
video_unregister_device(dcmi->vdev);
}
@@ -1570,12 +1749,31 @@
struct v4l2_async_subdev *asd)
{
struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+ unsigned int ret;
+ int src_pad;
- dev_dbg(dcmi->dev, "Subdev %s bound\n", subdev->name);
+ dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name);
- dcmi->entity.subdev = subdev;
+ /*
+ * Link this sub-device to DCMI, it could be
+ * a parallel camera sensor or a bridge
+ */
+ src_pad = media_entity_get_fwnode_pad(&subdev->entity,
+ subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
- return 0;
+ ret = media_create_pad_link(&subdev->entity, src_pad,
+ &dcmi->vdev->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ dev_err(dcmi->dev, "Failed to create media pad link with subdev \"%s\"\n",
+ subdev->name);
+ else
+ dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n",
+ subdev->name);
+
+ return ret;
}
static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = {
@@ -1599,7 +1797,7 @@
return -EINVAL;
/* Remote node to connect */
- dcmi->entity.node = remote;
+ dcmi->entity.remote_node = remote;
dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote);
return 0;
@@ -1607,33 +1805,31 @@
static int dcmi_graph_init(struct stm32_dcmi *dcmi)
{
- struct v4l2_async_subdev **subdevs = NULL;
int ret;
/* Parse the graph to extract a list of subdevice DT nodes. */
ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node);
if (ret < 0) {
- dev_err(dcmi->dev, "Graph parsing failed\n");
+ dev_err(dcmi->dev, "Failed to parse graph\n");
return ret;
}
- /* Register the subdevices notifier. */
- subdevs = devm_kzalloc(dcmi->dev, sizeof(*subdevs), GFP_KERNEL);
- if (!subdevs) {
- of_node_put(dcmi->entity.node);
- return -ENOMEM;
+ v4l2_async_notifier_init(&dcmi->notifier);
+
+ ret = v4l2_async_notifier_add_subdev(&dcmi->notifier,
+ &dcmi->entity.asd);
+ if (ret) {
+ dev_err(dcmi->dev, "Failed to add subdev notifier\n");
+ of_node_put(dcmi->entity.remote_node);
+ return ret;
}
- subdevs[0] = &dcmi->entity.asd;
-
- dcmi->notifier.subdevs = subdevs;
- dcmi->notifier.num_subdevs = 1;
dcmi->notifier.ops = &dcmi_graph_notify_ops;
ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier);
if (ret < 0) {
- dev_err(dcmi->dev, "Notifier registration failed\n");
- of_node_put(dcmi->entity.node);
+ dev_err(dcmi->dev, "Failed to register notifier\n");
+ v4l2_async_notifier_cleanup(&dcmi->notifier);
return ret;
}
@@ -1644,7 +1840,7 @@
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match = NULL;
- struct v4l2_fwnode_endpoint ep;
+ struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
struct stm32_dcmi *dcmi;
struct vb2_queue *q;
struct dma_chan *chan;
@@ -1672,7 +1868,6 @@
np = of_graph_get_next_endpoint(np, NULL);
if (!np) {
dev_err(&pdev->dev, "Could not find the endpoint\n");
- of_node_put(np);
return -ENODEV;
}
@@ -1683,7 +1878,7 @@
return ret;
}
- if (ep.bus_type == V4L2_MBUS_CSI2) {
+ if (ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
dev_err(&pdev->dev, "CSI bus not supported\n");
return -ENODEV;
}
@@ -1692,11 +1887,10 @@
dcmi->bus.data_shift = ep.bus.parallel.data_shift;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get irq\n");
+ if (irq <= 0)
return irq ? irq : -ENXIO;
- }
+
+ dcmi->irq = irq;
dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!dcmi->res) {
@@ -1710,14 +1904,6 @@
return PTR_ERR(dcmi->regs);
}
- ret = devm_request_threaded_irq(&pdev->dev, irq, dcmi_irq_callback,
- dcmi_irq_thread, IRQF_ONESHOT,
- dev_name(&pdev->dev), dcmi);
- if (ret) {
- dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
- return ret;
- }
-
mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(mclk)) {
if (PTR_ERR(mclk) != -EPROBE_DEFER)
@@ -1725,10 +1911,13 @@
return PTR_ERR(mclk);
}
- chan = dma_request_slave_channel(&pdev->dev, "tx");
- if (!chan) {
- dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n");
- return -EPROBE_DEFER;
+ chan = dma_request_chan(&pdev->dev, "tx");
+ if (IS_ERR(chan)) {
+ ret = PTR_ERR(chan);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Failed to request DMA channel: %d\n", ret);
+ return ret;
}
spin_lock_init(&dcmi->irqlock);
@@ -1744,10 +1933,19 @@
q = &dcmi->queue;
+ dcmi->v4l2_dev.mdev = &dcmi->mdev;
+
+ /* Initialize media device */
+ strscpy(dcmi->mdev.model, DRV_NAME, sizeof(dcmi->mdev.model));
+ snprintf(dcmi->mdev.bus_info, sizeof(dcmi->mdev.bus_info),
+ "platform:%s", DRV_NAME);
+ dcmi->mdev.dev = &pdev->dev;
+ media_device_init(&dcmi->mdev);
+
/* Initialize the top-level structure */
ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev);
if (ret)
- goto err_dma_release;
+ goto err_media_device_cleanup;
dcmi->vdev = video_device_alloc();
if (!dcmi->vdev) {
@@ -1759,13 +1957,32 @@
dcmi->vdev->fops = &dcmi_fops;
dcmi->vdev->v4l2_dev = &dcmi->v4l2_dev;
dcmi->vdev->queue = &dcmi->queue;
- strlcpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name));
+ strscpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name));
dcmi->vdev->release = video_device_release;
dcmi->vdev->ioctl_ops = &dcmi_ioctl_ops;
dcmi->vdev->lock = &dcmi->lock;
dcmi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
video_set_drvdata(dcmi->vdev, dcmi);
+
+ /* Media entity pads */
+ dcmi->vid_cap_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&dcmi->vdev->entity,
+ 1, &dcmi->vid_cap_pad);
+ if (ret) {
+ dev_err(dcmi->dev, "Failed to init media entity pad\n");
+ goto err_device_release;
+ }
+ dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(dcmi->dev, "Failed to register video device\n");
+ goto err_media_entity_cleanup;
+ }
+
+ dev_dbg(dcmi->dev, "Device registered as %s\n",
+ video_device_node_name(dcmi->vdev));
/* Buffer queue */
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1782,18 +1999,18 @@
ret = vb2_queue_init(q);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize vb2 queue\n");
- goto err_device_release;
+ goto err_media_entity_cleanup;
}
ret = dcmi_graph_init(dcmi);
if (ret < 0)
- goto err_device_release;
+ goto err_media_entity_cleanup;
/* Reset device */
ret = reset_control_assert(dcmi->rstc);
if (ret) {
dev_err(&pdev->dev, "Failed to assert the reset line\n");
- goto err_device_release;
+ goto err_cleanup;
}
usleep_range(3000, 5000);
@@ -1801,7 +2018,7 @@
ret = reset_control_deassert(dcmi->rstc);
if (ret) {
dev_err(&pdev->dev, "Failed to deassert the reset line\n");
- goto err_device_release;
+ goto err_cleanup;
}
dev_info(&pdev->dev, "Probe done\n");
@@ -1812,11 +2029,16 @@
return 0;
+err_cleanup:
+ v4l2_async_notifier_cleanup(&dcmi->notifier);
+err_media_entity_cleanup:
+ media_entity_cleanup(&dcmi->vdev->entity);
err_device_release:
video_device_release(dcmi->vdev);
err_device_unregister:
v4l2_device_unregister(&dcmi->v4l2_dev);
-err_dma_release:
+err_media_device_cleanup:
+ media_device_cleanup(&dcmi->mdev);
dma_release_channel(dcmi->dma_chan);
return ret;
@@ -1829,7 +2051,10 @@
pm_runtime_disable(&pdev->dev);
v4l2_async_notifier_unregister(&dcmi->notifier);
+ v4l2_async_notifier_cleanup(&dcmi->notifier);
+ media_entity_cleanup(&dcmi->vdev->entity);
v4l2_device_unregister(&dcmi->v4l2_dev);
+ media_device_cleanup(&dcmi->mdev);
dma_release_channel(dcmi->dma_chan);
--
Gitblit v1.6.2