|
@@ -633,6 +633,12 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
|
cdwdr_width *= 2;
|
|
cdwdr_width *= 2;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* CSI2 special configuration */
|
|
|
|
+ if (pcdev->pdata->csi2_dev) {
|
|
|
|
+ in_width = ((in_width - 2) * 2);
|
|
|
|
+ left_offset *= 2;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
|
|
/* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
|
|
camor = left_offset | (top_offset << 16);
|
|
camor = left_offset | (top_offset << 16);
|
|
|
|
|
|
@@ -767,6 +773,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
|
value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
|
value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
|
value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
|
value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
|
value |= pcdev->is_16bit ? 1 << 12 : 0;
|
|
value |= pcdev->is_16bit ? 1 << 12 : 0;
|
|
|
|
+
|
|
|
|
+ /* CSI2 mode */
|
|
|
|
+ if (pcdev->pdata->csi2_dev)
|
|
|
|
+ value |= 3 << 12;
|
|
|
|
+
|
|
ceu_write(pcdev, CAMCR, value);
|
|
ceu_write(pcdev, CAMCR, value);
|
|
|
|
|
|
ceu_write(pcdev, CAPCR, 0x00300000);
|
|
ceu_write(pcdev, CAPCR, 0x00300000);
|
|
@@ -883,6 +894,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
|
{
|
|
{
|
|
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
|
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
|
struct device *dev = icd->dev.parent;
|
|
struct device *dev = icd->dev.parent;
|
|
|
|
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
|
|
|
|
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
|
int ret, k, n;
|
|
int ret, k, n;
|
|
int formats = 0;
|
|
int formats = 0;
|
|
struct sh_mobile_ceu_cam *cam;
|
|
struct sh_mobile_ceu_cam *cam;
|
|
@@ -896,19 +909,19 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
|
|
|
|
|
fmt = soc_mbus_get_fmtdesc(code);
|
|
fmt = soc_mbus_get_fmtdesc(code);
|
|
if (!fmt) {
|
|
if (!fmt) {
|
|
- dev_err(icd->dev.parent,
|
|
|
|
- "Invalid format code #%u: %d\n", idx, code);
|
|
|
|
|
|
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
|
|
|
|
- if (ret < 0)
|
|
|
|
- return 0;
|
|
|
|
|
|
+ if (!pcdev->pdata->csi2_dev) {
|
|
|
|
+ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
if (!icd->host_priv) {
|
|
if (!icd->host_priv) {
|
|
struct v4l2_mbus_framefmt mf;
|
|
struct v4l2_mbus_framefmt mf;
|
|
struct v4l2_rect rect;
|
|
struct v4l2_rect rect;
|
|
- struct device *dev = icd->dev.parent;
|
|
|
|
int shift = 0;
|
|
int shift = 0;
|
|
|
|
|
|
/* FIXME: subwindow is lost between close / open */
|
|
/* FIXME: subwindow is lost between close / open */
|
|
@@ -927,7 +940,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
|
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
|
|
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
|
|
mf.width = 2560 >> shift;
|
|
mf.width = 2560 >> shift;
|
|
mf.height = 1920 >> shift;
|
|
mf.height = 1920 >> shift;
|
|
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
|
|
|
|
|
|
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
|
|
|
|
+ s_mbus_fmt, &mf);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
shift++;
|
|
shift++;
|
|
@@ -1228,7 +1242,8 @@ static int client_s_fmt(struct soc_camera_device *icd,
|
|
struct v4l2_cropcap cap;
|
|
struct v4l2_cropcap cap;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf);
|
|
|
|
|
|
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
|
|
|
|
+ s_mbus_fmt, mf);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
@@ -1257,7 +1272,8 @@ static int client_s_fmt(struct soc_camera_device *icd,
|
|
tmp_h = min(2 * tmp_h, max_height);
|
|
tmp_h = min(2 * tmp_h, max_height);
|
|
mf->width = tmp_w;
|
|
mf->width = tmp_w;
|
|
mf->height = tmp_h;
|
|
mf->height = tmp_h;
|
|
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf);
|
|
|
|
|
|
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
|
|
|
|
+ s_mbus_fmt, mf);
|
|
dev_geo(dev, "Camera scaled to %ux%u\n",
|
|
dev_geo(dev, "Camera scaled to %ux%u\n",
|
|
mf->width, mf->height);
|
|
mf->width, mf->height);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
@@ -1514,7 +1530,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
|
struct device *dev = icd->dev.parent;
|
|
struct device *dev = icd->dev.parent;
|
|
__u32 pixfmt = pix->pixelformat;
|
|
__u32 pixfmt = pix->pixelformat;
|
|
const struct soc_camera_format_xlate *xlate;
|
|
const struct soc_camera_format_xlate *xlate;
|
|
- unsigned int ceu_sub_width, ceu_sub_height;
|
|
|
|
|
|
+ /* Keep Compiler Happy */
|
|
|
|
+ unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
|
|
u16 scale_v, scale_h;
|
|
u16 scale_v, scale_h;
|
|
int ret;
|
|
int ret;
|
|
bool image_mode;
|
|
bool image_mode;
|
|
@@ -1569,8 +1586,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
|
|
|
|
|
/* Done with the camera. Now see if we can improve the result */
|
|
/* Done with the camera. Now see if we can improve the result */
|
|
|
|
|
|
- dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n",
|
|
|
|
- ret, mf.width, mf.height, pix->width, pix->height);
|
|
|
|
|
|
+ dev_geo(dev, "fmt %ux%u, requested %ux%u\n",
|
|
|
|
+ mf.width, mf.height, pix->width, pix->height);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
@@ -1634,6 +1651,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
|
int width, height;
|
|
int width, height;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
+ dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
|
|
|
|
+ pixfmt, pix->width, pix->height);
|
|
|
|
+
|
|
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
|
|
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
|
|
if (!xlate) {
|
|
if (!xlate) {
|
|
dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
|
|
dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
|
|
@@ -1660,7 +1680,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
|
mf.code = xlate->code;
|
|
mf.code = xlate->code;
|
|
mf.colorspace = pix->colorspace;
|
|
mf.colorspace = pix->colorspace;
|
|
|
|
|
|
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
|
|
|
|
|
|
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
@@ -1684,7 +1704,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
|
*/
|
|
*/
|
|
mf.width = 2560;
|
|
mf.width = 2560;
|
|
mf.height = 1920;
|
|
mf.height = 1920;
|
|
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
|
|
|
|
|
|
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
|
|
|
|
+ try_mbus_fmt, &mf);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
/* Shouldn't actually happen... */
|
|
/* Shouldn't actually happen... */
|
|
dev_err(icd->dev.parent,
|
|
dev_err(icd->dev.parent,
|
|
@@ -1699,6 +1720,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
|
pix->height = height;
|
|
pix->height = height;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
|
|
|
|
+ __func__, ret, pix->pixelformat, pix->width, pix->height);
|
|
|
|
+
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1853,6 +1877,30 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
|
|
.num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
|
|
.num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct bus_wait {
|
|
|
|
+ struct notifier_block notifier;
|
|
|
|
+ struct completion completion;
|
|
|
|
+ struct device *dev;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int bus_notify(struct notifier_block *nb,
|
|
|
|
+ unsigned long action, void *data)
|
|
|
|
+{
|
|
|
|
+ struct device *dev = data;
|
|
|
|
+ struct bus_wait *wait = container_of(nb, struct bus_wait, notifier);
|
|
|
|
+
|
|
|
|
+ if (wait->dev != dev)
|
|
|
|
+ return NOTIFY_DONE;
|
|
|
|
+
|
|
|
|
+ switch (action) {
|
|
|
|
+ case BUS_NOTIFY_UNBOUND_DRIVER:
|
|
|
|
+ /* Protect from module unloading */
|
|
|
|
+ wait_for_completion(&wait->completion);
|
|
|
|
+ return NOTIFY_OK;
|
|
|
|
+ }
|
|
|
|
+ return NOTIFY_DONE;
|
|
|
|
+}
|
|
|
|
+
|
|
static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct sh_mobile_ceu_dev *pcdev;
|
|
struct sh_mobile_ceu_dev *pcdev;
|
|
@@ -1860,6 +1908,11 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
void __iomem *base;
|
|
void __iomem *base;
|
|
unsigned int irq;
|
|
unsigned int irq;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
+ struct bus_wait wait = {
|
|
|
|
+ .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
|
|
|
|
+ .notifier.notifier_call = bus_notify,
|
|
|
|
+ };
|
|
|
|
+ struct device *csi2;
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
irq = platform_get_irq(pdev, 0);
|
|
irq = platform_get_irq(pdev, 0);
|
|
@@ -1931,12 +1984,54 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
|
|
pcdev->ici.drv_name = dev_name(&pdev->dev);
|
|
pcdev->ici.drv_name = dev_name(&pdev->dev);
|
|
pcdev->ici.ops = &sh_mobile_ceu_host_ops;
|
|
pcdev->ici.ops = &sh_mobile_ceu_host_ops;
|
|
|
|
|
|
|
|
+ /* CSI2 interfacing */
|
|
|
|
+ csi2 = pcdev->pdata->csi2_dev;
|
|
|
|
+ if (csi2) {
|
|
|
|
+ wait.dev = csi2;
|
|
|
|
+
|
|
|
|
+ err = bus_register_notifier(&platform_bus_type, &wait.notifier);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto exit_free_clk;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * From this point the driver module will not unload, until
|
|
|
|
+ * we complete the completion.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ if (!csi2->driver || !csi2->driver->owner) {
|
|
|
|
+ complete(&wait.completion);
|
|
|
|
+ /* Either too late, or probing failed */
|
|
|
|
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
|
|
|
|
+ err = -ENXIO;
|
|
|
|
+ goto exit_free_clk;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The module is still loaded, in the worst case it is hanging
|
|
|
|
+ * in device release on our completion. So, _now_ dereferencing
|
|
|
|
+ * the "owner" is safe!
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ err = try_module_get(csi2->driver->owner);
|
|
|
|
+
|
|
|
|
+ /* Let notifier complete, if it has been locked */
|
|
|
|
+ complete(&wait.completion);
|
|
|
|
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
|
|
|
|
+ if (!err) {
|
|
|
|
+ err = -ENODEV;
|
|
|
|
+ goto exit_free_clk;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
err = soc_camera_host_register(&pcdev->ici);
|
|
err = soc_camera_host_register(&pcdev->ici);
|
|
if (err)
|
|
if (err)
|
|
- goto exit_free_clk;
|
|
|
|
|
|
+ goto exit_module_put;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+exit_module_put:
|
|
|
|
+ if (csi2 && csi2->driver)
|
|
|
|
+ module_put(csi2->driver->owner);
|
|
exit_free_clk:
|
|
exit_free_clk:
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
free_irq(pcdev->irq, pcdev);
|
|
free_irq(pcdev->irq, pcdev);
|
|
@@ -1956,6 +2051,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
|
|
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
|
|
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
|
|
struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
|
|
struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
|
|
struct sh_mobile_ceu_dev, ici);
|
|
struct sh_mobile_ceu_dev, ici);
|
|
|
|
+ struct device *csi2 = pcdev->pdata->csi2_dev;
|
|
|
|
|
|
soc_camera_host_unregister(soc_host);
|
|
soc_camera_host_unregister(soc_host);
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
@@ -1963,7 +2059,10 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
|
|
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
|
|
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
|
|
dma_release_declared_memory(&pdev->dev);
|
|
dma_release_declared_memory(&pdev->dev);
|
|
iounmap(pcdev->base);
|
|
iounmap(pcdev->base);
|
|
|
|
+ if (csi2 && csi2->driver)
|
|
|
|
+ module_put(csi2->driver->owner);
|
|
kfree(pcdev);
|
|
kfree(pcdev);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1995,6 +2094,8 @@ static struct platform_driver sh_mobile_ceu_driver = {
|
|
|
|
|
|
static int __init sh_mobile_ceu_init(void)
|
|
static int __init sh_mobile_ceu_init(void)
|
|
{
|
|
{
|
|
|
|
+ /* Whatever return code */
|
|
|
|
+ request_module("sh_mobile_csi2");
|
|
return platform_driver_register(&sh_mobile_ceu_driver);
|
|
return platform_driver_register(&sh_mobile_ceu_driver);
|
|
}
|
|
}
|
|
|
|
|