diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2010-07-26 11:13:34 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 15:43:40 -0400 |
commit | b3b5020d8c12037f030242aab8e272148bf1f472 (patch) | |
tree | ef61f21075c46a5c13252adccc50c303ed9dfd94 | |
parent | 077e2c10c9cb618d571bf16475db696610bdb24a (diff) |
V4L/DVB: V4L2: sh_mobile_camera_ceu: add support for CSI2
Using CEU with CSI2 on SH-Mobile requires some special configuration of the
former. We also have to switch from calling only one subdev .s_mbus_fmt and
.try_mbus_fmt to calling all subdevices. Take care to increment CSI2 driver
use count to prevent it from unloading, while in use.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 131 | ||||
-rw-r--r-- | include/media/sh_mobile_ceu.h | 3 |
2 files changed, 119 insertions, 15 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 86869dbcbab0..2b24bd0de3ad 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -633,6 +633,12 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) | |||
633 | cdwdr_width *= 2; | 633 | cdwdr_width *= 2; |
634 | } | 634 | } |
635 | 635 | ||
636 | /* CSI2 special configuration */ | ||
637 | if (pcdev->pdata->csi2_dev) { | ||
638 | in_width = ((in_width - 2) * 2); | ||
639 | left_offset *= 2; | ||
640 | } | ||
641 | |||
636 | /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ | 642 | /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ |
637 | camor = left_offset | (top_offset << 16); | 643 | camor = left_offset | (top_offset << 16); |
638 | 644 | ||
@@ -767,6 +773,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
767 | value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; | 773 | value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; |
768 | value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; | 774 | value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; |
769 | value |= pcdev->is_16bit ? 1 << 12 : 0; | 775 | value |= pcdev->is_16bit ? 1 << 12 : 0; |
776 | |||
777 | /* CSI2 mode */ | ||
778 | if (pcdev->pdata->csi2_dev) | ||
779 | value |= 3 << 12; | ||
780 | |||
770 | ceu_write(pcdev, CAMCR, value); | 781 | ceu_write(pcdev, CAMCR, value); |
771 | 782 | ||
772 | ceu_write(pcdev, CAPCR, 0x00300000); | 783 | ceu_write(pcdev, CAPCR, 0x00300000); |
@@ -883,6 +894,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int | |||
883 | { | 894 | { |
884 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | 895 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); |
885 | struct device *dev = icd->dev.parent; | 896 | struct device *dev = icd->dev.parent; |
897 | struct soc_camera_host *ici = to_soc_camera_host(dev); | ||
898 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
886 | int ret, k, n; | 899 | int ret, k, n; |
887 | int formats = 0; | 900 | int formats = 0; |
888 | struct sh_mobile_ceu_cam *cam; | 901 | struct sh_mobile_ceu_cam *cam; |
@@ -896,19 +909,19 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int | |||
896 | 909 | ||
897 | fmt = soc_mbus_get_fmtdesc(code); | 910 | fmt = soc_mbus_get_fmtdesc(code); |
898 | if (!fmt) { | 911 | if (!fmt) { |
899 | dev_err(icd->dev.parent, | 912 | dev_err(dev, "Invalid format code #%u: %d\n", idx, code); |
900 | "Invalid format code #%u: %d\n", idx, code); | ||
901 | return -EINVAL; | 913 | return -EINVAL; |
902 | } | 914 | } |
903 | 915 | ||
904 | ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); | 916 | if (!pcdev->pdata->csi2_dev) { |
905 | if (ret < 0) | 917 | ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); |
906 | return 0; | 918 | if (ret < 0) |
919 | return 0; | ||
920 | } | ||
907 | 921 | ||
908 | if (!icd->host_priv) { | 922 | if (!icd->host_priv) { |
909 | struct v4l2_mbus_framefmt mf; | 923 | struct v4l2_mbus_framefmt mf; |
910 | struct v4l2_rect rect; | 924 | struct v4l2_rect rect; |
911 | struct device *dev = icd->dev.parent; | ||
912 | int shift = 0; | 925 | int shift = 0; |
913 | 926 | ||
914 | /* FIXME: subwindow is lost between close / open */ | 927 | /* 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 | |||
927 | /* Try 2560x1920, 1280x960, 640x480, 320x240 */ | 940 | /* Try 2560x1920, 1280x960, 640x480, 320x240 */ |
928 | mf.width = 2560 >> shift; | 941 | mf.width = 2560 >> shift; |
929 | mf.height = 1920 >> shift; | 942 | mf.height = 1920 >> shift; |
930 | ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); | 943 | ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, |
944 | s_mbus_fmt, &mf); | ||
931 | if (ret < 0) | 945 | if (ret < 0) |
932 | return ret; | 946 | return ret; |
933 | shift++; | 947 | shift++; |
@@ -1228,7 +1242,8 @@ static int client_s_fmt(struct soc_camera_device *icd, | |||
1228 | struct v4l2_cropcap cap; | 1242 | struct v4l2_cropcap cap; |
1229 | int ret; | 1243 | int ret; |
1230 | 1244 | ||
1231 | ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); | 1245 | ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, |
1246 | s_mbus_fmt, mf); | ||
1232 | if (ret < 0) | 1247 | if (ret < 0) |
1233 | return ret; | 1248 | return ret; |
1234 | 1249 | ||
@@ -1257,7 +1272,8 @@ static int client_s_fmt(struct soc_camera_device *icd, | |||
1257 | tmp_h = min(2 * tmp_h, max_height); | 1272 | tmp_h = min(2 * tmp_h, max_height); |
1258 | mf->width = tmp_w; | 1273 | mf->width = tmp_w; |
1259 | mf->height = tmp_h; | 1274 | mf->height = tmp_h; |
1260 | ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); | 1275 | ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, |
1276 | s_mbus_fmt, mf); | ||
1261 | dev_geo(dev, "Camera scaled to %ux%u\n", | 1277 | dev_geo(dev, "Camera scaled to %ux%u\n", |
1262 | mf->width, mf->height); | 1278 | mf->width, mf->height); |
1263 | if (ret < 0) { | 1279 | if (ret < 0) { |
@@ -1514,7 +1530,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, | |||
1514 | struct device *dev = icd->dev.parent; | 1530 | struct device *dev = icd->dev.parent; |
1515 | __u32 pixfmt = pix->pixelformat; | 1531 | __u32 pixfmt = pix->pixelformat; |
1516 | const struct soc_camera_format_xlate *xlate; | 1532 | const struct soc_camera_format_xlate *xlate; |
1517 | unsigned int ceu_sub_width, ceu_sub_height; | 1533 | /* Keep Compiler Happy */ |
1534 | unsigned int ceu_sub_width = 0, ceu_sub_height = 0; | ||
1518 | u16 scale_v, scale_h; | 1535 | u16 scale_v, scale_h; |
1519 | int ret; | 1536 | int ret; |
1520 | bool image_mode; | 1537 | bool image_mode; |
@@ -1569,8 +1586,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, | |||
1569 | 1586 | ||
1570 | /* Done with the camera. Now see if we can improve the result */ | 1587 | /* Done with the camera. Now see if we can improve the result */ |
1571 | 1588 | ||
1572 | dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n", | 1589 | dev_geo(dev, "fmt %ux%u, requested %ux%u\n", |
1573 | ret, mf.width, mf.height, pix->width, pix->height); | 1590 | mf.width, mf.height, pix->width, pix->height); |
1574 | if (ret < 0) | 1591 | if (ret < 0) |
1575 | return ret; | 1592 | return ret; |
1576 | 1593 | ||
@@ -1634,6 +1651,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
1634 | int width, height; | 1651 | int width, height; |
1635 | int ret; | 1652 | int ret; |
1636 | 1653 | ||
1654 | dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n", | ||
1655 | pixfmt, pix->width, pix->height); | ||
1656 | |||
1637 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | 1657 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
1638 | if (!xlate) { | 1658 | if (!xlate) { |
1639 | dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); | 1659 | 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, | |||
1660 | mf.code = xlate->code; | 1680 | mf.code = xlate->code; |
1661 | mf.colorspace = pix->colorspace; | 1681 | mf.colorspace = pix->colorspace; |
1662 | 1682 | ||
1663 | ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); | 1683 | ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf); |
1664 | if (ret < 0) | 1684 | if (ret < 0) |
1665 | return ret; | 1685 | return ret; |
1666 | 1686 | ||
@@ -1684,7 +1704,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
1684 | */ | 1704 | */ |
1685 | mf.width = 2560; | 1705 | mf.width = 2560; |
1686 | mf.height = 1920; | 1706 | mf.height = 1920; |
1687 | ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); | 1707 | ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, |
1708 | try_mbus_fmt, &mf); | ||
1688 | if (ret < 0) { | 1709 | if (ret < 0) { |
1689 | /* Shouldn't actually happen... */ | 1710 | /* Shouldn't actually happen... */ |
1690 | dev_err(icd->dev.parent, | 1711 | dev_err(icd->dev.parent, |
@@ -1699,6 +1720,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
1699 | pix->height = height; | 1720 | pix->height = height; |
1700 | } | 1721 | } |
1701 | 1722 | ||
1723 | dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n", | ||
1724 | __func__, ret, pix->pixelformat, pix->width, pix->height); | ||
1725 | |||
1702 | return ret; | 1726 | return ret; |
1703 | } | 1727 | } |
1704 | 1728 | ||
@@ -1853,6 +1877,30 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { | |||
1853 | .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), | 1877 | .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), |
1854 | }; | 1878 | }; |
1855 | 1879 | ||
1880 | struct bus_wait { | ||
1881 | struct notifier_block notifier; | ||
1882 | struct completion completion; | ||
1883 | struct device *dev; | ||
1884 | }; | ||
1885 | |||
1886 | static int bus_notify(struct notifier_block *nb, | ||
1887 | unsigned long action, void *data) | ||
1888 | { | ||
1889 | struct device *dev = data; | ||
1890 | struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); | ||
1891 | |||
1892 | if (wait->dev != dev) | ||
1893 | return NOTIFY_DONE; | ||
1894 | |||
1895 | switch (action) { | ||
1896 | case BUS_NOTIFY_UNBOUND_DRIVER: | ||
1897 | /* Protect from module unloading */ | ||
1898 | wait_for_completion(&wait->completion); | ||
1899 | return NOTIFY_OK; | ||
1900 | } | ||
1901 | return NOTIFY_DONE; | ||
1902 | } | ||
1903 | |||
1856 | static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | 1904 | static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) |
1857 | { | 1905 | { |
1858 | struct sh_mobile_ceu_dev *pcdev; | 1906 | struct sh_mobile_ceu_dev *pcdev; |
@@ -1860,6 +1908,11 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | |||
1860 | void __iomem *base; | 1908 | void __iomem *base; |
1861 | unsigned int irq; | 1909 | unsigned int irq; |
1862 | int err = 0; | 1910 | int err = 0; |
1911 | struct bus_wait wait = { | ||
1912 | .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), | ||
1913 | .notifier.notifier_call = bus_notify, | ||
1914 | }; | ||
1915 | struct device *csi2; | ||
1863 | 1916 | ||
1864 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1917 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1865 | irq = platform_get_irq(pdev, 0); | 1918 | irq = platform_get_irq(pdev, 0); |
@@ -1931,12 +1984,54 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | |||
1931 | pcdev->ici.drv_name = dev_name(&pdev->dev); | 1984 | pcdev->ici.drv_name = dev_name(&pdev->dev); |
1932 | pcdev->ici.ops = &sh_mobile_ceu_host_ops; | 1985 | pcdev->ici.ops = &sh_mobile_ceu_host_ops; |
1933 | 1986 | ||
1987 | /* CSI2 interfacing */ | ||
1988 | csi2 = pcdev->pdata->csi2_dev; | ||
1989 | if (csi2) { | ||
1990 | wait.dev = csi2; | ||
1991 | |||
1992 | err = bus_register_notifier(&platform_bus_type, &wait.notifier); | ||
1993 | if (err < 0) | ||
1994 | goto exit_free_clk; | ||
1995 | |||
1996 | /* | ||
1997 | * From this point the driver module will not unload, until | ||
1998 | * we complete the completion. | ||
1999 | */ | ||
2000 | |||
2001 | if (!csi2->driver || !csi2->driver->owner) { | ||
2002 | complete(&wait.completion); | ||
2003 | /* Either too late, or probing failed */ | ||
2004 | bus_unregister_notifier(&platform_bus_type, &wait.notifier); | ||
2005 | err = -ENXIO; | ||
2006 | goto exit_free_clk; | ||
2007 | } | ||
2008 | |||
2009 | /* | ||
2010 | * The module is still loaded, in the worst case it is hanging | ||
2011 | * in device release on our completion. So, _now_ dereferencing | ||
2012 | * the "owner" is safe! | ||
2013 | */ | ||
2014 | |||
2015 | err = try_module_get(csi2->driver->owner); | ||
2016 | |||
2017 | /* Let notifier complete, if it has been locked */ | ||
2018 | complete(&wait.completion); | ||
2019 | bus_unregister_notifier(&platform_bus_type, &wait.notifier); | ||
2020 | if (!err) { | ||
2021 | err = -ENODEV; | ||
2022 | goto exit_free_clk; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
1934 | err = soc_camera_host_register(&pcdev->ici); | 2026 | err = soc_camera_host_register(&pcdev->ici); |
1935 | if (err) | 2027 | if (err) |
1936 | goto exit_free_clk; | 2028 | goto exit_module_put; |
1937 | 2029 | ||
1938 | return 0; | 2030 | return 0; |
1939 | 2031 | ||
2032 | exit_module_put: | ||
2033 | if (csi2 && csi2->driver) | ||
2034 | module_put(csi2->driver->owner); | ||
1940 | exit_free_clk: | 2035 | exit_free_clk: |
1941 | pm_runtime_disable(&pdev->dev); | 2036 | pm_runtime_disable(&pdev->dev); |
1942 | free_irq(pcdev->irq, pcdev); | 2037 | free_irq(pcdev->irq, pcdev); |
@@ -1956,6 +2051,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) | |||
1956 | struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); | 2051 | struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); |
1957 | struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, | 2052 | struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, |
1958 | struct sh_mobile_ceu_dev, ici); | 2053 | struct sh_mobile_ceu_dev, ici); |
2054 | struct device *csi2 = pcdev->pdata->csi2_dev; | ||
1959 | 2055 | ||
1960 | soc_camera_host_unregister(soc_host); | 2056 | soc_camera_host_unregister(soc_host); |
1961 | pm_runtime_disable(&pdev->dev); | 2057 | pm_runtime_disable(&pdev->dev); |
@@ -1963,7 +2059,10 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) | |||
1963 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) | 2059 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) |
1964 | dma_release_declared_memory(&pdev->dev); | 2060 | dma_release_declared_memory(&pdev->dev); |
1965 | iounmap(pcdev->base); | 2061 | iounmap(pcdev->base); |
2062 | if (csi2 && csi2->driver) | ||
2063 | module_put(csi2->driver->owner); | ||
1966 | kfree(pcdev); | 2064 | kfree(pcdev); |
2065 | |||
1967 | return 0; | 2066 | return 0; |
1968 | } | 2067 | } |
1969 | 2068 | ||
@@ -1995,6 +2094,8 @@ static struct platform_driver sh_mobile_ceu_driver = { | |||
1995 | 2094 | ||
1996 | static int __init sh_mobile_ceu_init(void) | 2095 | static int __init sh_mobile_ceu_init(void) |
1997 | { | 2096 | { |
2097 | /* Whatever return code */ | ||
2098 | request_module("sh_mobile_csi2"); | ||
1998 | return platform_driver_register(&sh_mobile_ceu_driver); | 2099 | return platform_driver_register(&sh_mobile_ceu_driver); |
1999 | } | 2100 | } |
2000 | 2101 | ||
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h index b67747836878..80346a6d28a9 100644 --- a/include/media/sh_mobile_ceu.h +++ b/include/media/sh_mobile_ceu.h | |||
@@ -6,8 +6,11 @@ | |||
6 | #define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ | 6 | #define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ |
7 | #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ | 7 | #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ |
8 | 8 | ||
9 | struct device; | ||
10 | |||
9 | struct sh_mobile_ceu_info { | 11 | struct sh_mobile_ceu_info { |
10 | unsigned long flags; | 12 | unsigned long flags; |
13 | struct device *csi2_dev; | ||
11 | }; | 14 | }; |
12 | 15 | ||
13 | #endif /* __ASM_SH_MOBILE_CEU_H__ */ | 16 | #endif /* __ASM_SH_MOBILE_CEU_H__ */ |