|
@@ -36,6 +36,7 @@
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/sched.h>
|
|
|
|
|
|
+#include <media/v4l2-async.h>
|
|
|
#include <media/v4l2-common.h>
|
|
|
#include <media/v4l2-dev.h>
|
|
|
#include <media/soc_camera.h>
|
|
@@ -96,6 +97,10 @@ struct sh_mobile_ceu_buffer {
|
|
|
|
|
|
struct sh_mobile_ceu_dev {
|
|
|
struct soc_camera_host ici;
|
|
|
+ /* Asynchronous CSI2 linking */
|
|
|
+ struct v4l2_async_subdev *csi2_asd;
|
|
|
+ struct v4l2_subdev *csi2_sd;
|
|
|
+ /* Synchronous probing compatibility */
|
|
|
struct platform_device *csi2_pdev;
|
|
|
|
|
|
unsigned int irq;
|
|
@@ -185,7 +190,6 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
|
|
|
udelay(1);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
if (2 != success) {
|
|
|
dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
|
|
|
return -EIO;
|
|
@@ -534,16 +538,29 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
|
|
|
{
|
|
|
struct v4l2_subdev *sd;
|
|
|
|
|
|
- if (!pcdev->csi2_pdev)
|
|
|
- return NULL;
|
|
|
+ if (pcdev->csi2_sd)
|
|
|
+ return pcdev->csi2_sd;
|
|
|
|
|
|
- v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
|
|
|
- if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
|
|
|
- return sd;
|
|
|
+ if (pcdev->csi2_asd) {
|
|
|
+ char name[] = "sh-mobile-csi2";
|
|
|
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
|
|
|
+ if (!strncmp(name, sd->name, sizeof(name) - 1)) {
|
|
|
+ pcdev->csi2_sd = sd;
|
|
|
+ return sd;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev,
|
|
|
+ struct soc_camera_device *icd)
|
|
|
+{
|
|
|
+ struct v4l2_subdev *sd = pcdev->csi2_sd;
|
|
|
+
|
|
|
+ return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL;
|
|
|
+}
|
|
|
+
|
|
|
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
|
|
|
{
|
|
|
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
@@ -564,12 +581,12 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
|
|
|
* -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
|
|
|
* has not found this soc-camera device among its clients
|
|
|
*/
|
|
|
- if (ret == -ENODEV && csi2_sd)
|
|
|
+ if (csi2_sd && ret == -ENODEV)
|
|
|
csi2_sd->grp_id = 0;
|
|
|
|
|
|
dev_info(icd->parent,
|
|
|
- "SuperH Mobile CEU driver attached to camera %d\n",
|
|
|
- icd->devnum);
|
|
|
+ "SuperH Mobile CEU%s driver attached to camera %d\n",
|
|
|
+ csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -585,8 +602,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
|
|
|
icd->devnum);
|
|
|
|
|
|
v4l2_subdev_call(csi2_sd, core, s_power, 0);
|
|
|
- if (csi2_sd)
|
|
|
- csi2_sd->grp_id = 0;
|
|
|
}
|
|
|
|
|
|
/* Called with .host_lock held */
|
|
@@ -708,7 +723,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
|
|
}
|
|
|
|
|
|
/* CSI2 special configuration */
|
|
|
- if (pcdev->csi2_pdev) {
|
|
|
+ if (csi2_subdev(pcdev, icd)) {
|
|
|
in_width = ((in_width - 2) * 2);
|
|
|
left_offset *= 2;
|
|
|
}
|
|
@@ -765,13 +780,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
|
|
|
static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
|
|
|
struct soc_camera_device *icd)
|
|
|
{
|
|
|
- if (pcdev->csi2_pdev) {
|
|
|
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
|
|
|
- if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
|
|
|
- return csi2_sd;
|
|
|
- }
|
|
|
-
|
|
|
- return soc_camera_to_subdev(icd);
|
|
|
+ return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd);
|
|
|
}
|
|
|
|
|
|
#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
|
@@ -875,7 +884,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
|
|
|
value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
|
|
value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
|
|
|
|
|
- if (pcdev->csi2_pdev) /* CSI2 mode */
|
|
|
+ if (csi2_subdev(pcdev, icd)) /* CSI2 mode */
|
|
|
value |= 3 << 12;
|
|
|
else if (pcdev->is_16bit)
|
|
|
value |= 1 << 12;
|
|
@@ -1054,7 +1063,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (!pcdev->pdata || !pcdev->pdata->csi2) {
|
|
|
+ if (!csi2_subdev(pcdev, icd)) {
|
|
|
/* Are there any restrictions in the CSI-2 case? */
|
|
|
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
|
|
|
if (ret < 0)
|
|
@@ -2084,7 +2093,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
|
struct resource *res;
|
|
|
void __iomem *base;
|
|
|
unsigned int irq;
|
|
|
- int err = 0;
|
|
|
+ int err, i;
|
|
|
struct bus_wait wait = {
|
|
|
.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
|
|
|
.notifier.notifier_call = bus_notify,
|
|
@@ -2188,31 +2197,60 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
|
goto exit_free_clk;
|
|
|
}
|
|
|
|
|
|
- err = soc_camera_host_register(&pcdev->ici);
|
|
|
- if (err)
|
|
|
- goto exit_free_ctx;
|
|
|
+ if (pcdev->pdata && pcdev->pdata->asd_sizes) {
|
|
|
+ struct v4l2_async_subdev **asd;
|
|
|
+ char name[] = "sh-mobile-csi2";
|
|
|
+ int j;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * CSI2 interfacing: several groups can use CSI2, pick up the
|
|
|
+ * first one
|
|
|
+ */
|
|
|
+ asd = pcdev->pdata->asd;
|
|
|
+ for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
|
|
|
+ for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
|
|
|
+ dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
|
|
|
+ __func__, i, (*asd)->bus_type);
|
|
|
+ if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
|
|
|
+ !strncmp(name, (*asd)->match.platform.name,
|
|
|
+ sizeof(name) - 1)) {
|
|
|
+ pcdev->csi2_asd = *asd;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pcdev->csi2_asd)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ pcdev->ici.asd = pcdev->pdata->asd;
|
|
|
+ pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
|
|
|
+ }
|
|
|
|
|
|
- /* CSI2 interfacing */
|
|
|
+ /* Legacy CSI2 interfacing */
|
|
|
csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
|
|
|
if (csi2) {
|
|
|
+ /*
|
|
|
+ * TODO: remove this once all users are converted to
|
|
|
+ * asynchronous CSI2 probing. If it has to be kept, csi2
|
|
|
+ * platform device resources have to be added, using
|
|
|
+ * platform_device_add_resources()
|
|
|
+ */
|
|
|
struct platform_device *csi2_pdev =
|
|
|
platform_device_alloc("sh-mobile-csi2", csi2->id);
|
|
|
struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
|
|
|
|
|
|
if (!csi2_pdev) {
|
|
|
err = -ENOMEM;
|
|
|
- goto exit_host_unregister;
|
|
|
+ goto exit_free_ctx;
|
|
|
}
|
|
|
|
|
|
pcdev->csi2_pdev = csi2_pdev;
|
|
|
|
|
|
- err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
|
|
|
+ err = platform_device_add_data(csi2_pdev, csi2_pdata,
|
|
|
+ sizeof(*csi2_pdata));
|
|
|
if (err < 0)
|
|
|
goto exit_pdev_put;
|
|
|
|
|
|
- csi2_pdata = csi2_pdev->dev.platform_data;
|
|
|
- csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
|
|
|
-
|
|
|
csi2_pdev->resource = csi2->resource;
|
|
|
csi2_pdev->num_resources = csi2->num_resources;
|
|
|
|
|
@@ -2254,17 +2292,38 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
|
err = -ENODEV;
|
|
|
goto exit_pdev_unregister;
|
|
|
}
|
|
|
+
|
|
|
+ pcdev->csi2_sd = platform_get_drvdata(csi2_pdev);
|
|
|
+ }
|
|
|
+
|
|
|
+ err = soc_camera_host_register(&pcdev->ici);
|
|
|
+ if (err)
|
|
|
+ goto exit_csi2_unregister;
|
|
|
+
|
|
|
+ if (csi2) {
|
|
|
+ err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev,
|
|
|
+ pcdev->csi2_sd);
|
|
|
+ dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n",
|
|
|
+ __func__, err);
|
|
|
+ if (err < 0)
|
|
|
+ goto exit_host_unregister;
|
|
|
+ /* v4l2_device_register_subdev() took a reference too */
|
|
|
+ module_put(pcdev->csi2_sd->owner);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-exit_pdev_unregister:
|
|
|
- platform_device_del(pcdev->csi2_pdev);
|
|
|
-exit_pdev_put:
|
|
|
- pcdev->csi2_pdev->resource = NULL;
|
|
|
- platform_device_put(pcdev->csi2_pdev);
|
|
|
exit_host_unregister:
|
|
|
soc_camera_host_unregister(&pcdev->ici);
|
|
|
+exit_csi2_unregister:
|
|
|
+ if (csi2) {
|
|
|
+ module_put(pcdev->csi2_pdev->dev.driver->owner);
|
|
|
+exit_pdev_unregister:
|
|
|
+ platform_device_del(pcdev->csi2_pdev);
|
|
|
+exit_pdev_put:
|
|
|
+ pcdev->csi2_pdev->resource = NULL;
|
|
|
+ platform_device_put(pcdev->csi2_pdev);
|
|
|
+ }
|
|
|
exit_free_ctx:
|
|
|
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
|
|
|
exit_free_clk:
|
|
@@ -2324,6 +2383,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
|
|
|
static struct platform_driver sh_mobile_ceu_driver = {
|
|
|
.driver = {
|
|
|
.name = "sh_mobile_ceu",
|
|
|
+ .owner = THIS_MODULE,
|
|
|
.pm = &sh_mobile_ceu_dev_pm_ops,
|
|
|
.of_match_table = sh_mobile_ceu_of_match,
|
|
|
},
|
|
@@ -2349,5 +2409,5 @@ module_exit(sh_mobile_ceu_exit);
|
|
|
MODULE_DESCRIPTION("SuperH Mobile CEU driver");
|
|
|
MODULE_AUTHOR("Magnus Damm");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
-MODULE_VERSION("0.0.6");
|
|
|
+MODULE_VERSION("0.1.0");
|
|
|
MODULE_ALIAS("platform:sh_mobile_ceu");
|