From 17121d12a5c1089bdfce429bc6f7ae2c25d9d25d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 23 May 2018 05:33:32 -0400 Subject: media: imx258: Check the rotation property has a value of 180 The driver only supports streaming images flipped horizontally and vertically. In order to ensure that all current users will be fine if or when support for upright streaming is added, require the presence of the "rotation" control now. Signed-off-by: Sakari Ailus Tested-by: "Lai, Jim" Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx258.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index f3b124723aa0..31a1e2294843 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1221,6 +1221,14 @@ static int imx258_probe(struct i2c_client *client) if (val != 19200000) return -EINVAL; + /* + * Check that the device is mounted upside down. The driver only + * supports a single pixel order right now. + */ + ret = device_property_read_u32(&client->dev, "rotation", &val); + if (ret || val != 180) + return -EINVAL; + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); if (!imx258) return -ENOMEM; -- cgit v1.2.3 From 3c46ab9d37e542dd28ee27ee31defe5f8b5e466c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 11:25:09 -0400 Subject: media: v4l: cadence: include linux/slab.h I ran into a randconfig build error with the new driver: drivers/media/platform/cadence/cdns-csi2tx.c: In function 'csi2tx_probe': drivers/media/platform/cadence/cdns-csi2tx.c:477:11: error: implicit declaration of function 'kzalloc'; did you mean 'd_alloc'? [-Werror=implicit-function-declaration] kzalloc() is declared in linux/slab.h, so let's include this to make it build in all configurations. Fixes: 84b477e6d4bc ("media: v4l: cadence: Add Cadence MIPI-CSI2 TX driver") Signed-off-by: Arnd Bergmann Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 1 + drivers/media/platform/cadence/cdns-csi2tx.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index a0f02916006b..43e43c7b3e98 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index dfa1d88d955b..40d0de690ff4 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From f6f630327de95c251741c1f682a94243c48f2b13 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 May 2018 18:07:10 -0400 Subject: media: v4l: cadence: add VIDEO_V4L2 dependency The cadence media drivers can be built-in while the v4l2 core is a loadable module. This is a mistake and leads to link errors: drivers/media/v4l2-core/v4l2-fwnode.o: In function `v4l2_async_register_subdev_sensor_common': v4l2-fwnode.c:(.text+0x12f0): undefined reference to `v4l2_async_subdev_notifier_register' v4l2-fwnode.c:(.text+0x1304): undefined reference to `v4l2_async_register_subdev' v4l2-fwnode.c:(.text+0x1318): undefined reference to `v4l2_async_notifier_unregister' v4l2-fwnode.c:(.text+0x1338): undefined reference to `v4l2_async_notifier_cleanup' cdns-csi2rx.c:(.text+0x9f8): undefined reference to `v4l2_subdev_init' cdns-csi2rx.c:(.text+0xa78): undefined reference to `v4l2_async_register_subdev' drivers/media/platform/cadence/cdns-csi2tx.o: In function `csi2tx_remove': cdns-csi2tx.c:(.text+0x88): undefined reference to `v4l2_async_unregister_subdev' drivers/media/platform/cadence/cdns-csi2tx.o: In function `csi2tx_probe': cdns-csi2tx.c:(.text+0x884): undefined reference to `v4l2_subdev_init' cdns-csi2tx.c:(.text+0xa9c): undefined reference to `v4l2_async_register_subdev' An explicit Kconfig dependency on VIDEO_V4L2 avoids the problem. Fixes: 1fc3b37f34f6 ("media: v4l: cadence: Add Cadence MIPI-CSI2 RX driver") Signed-off-by: Arnd Bergmann Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 3bf0f2454384..cf6124da3c54 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -11,6 +11,7 @@ if VIDEO_CADENCE config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" + depends on VIDEO_V4L2 depends on MEDIA_CONTROLLER depends on VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -22,6 +23,7 @@ config VIDEO_CADENCE_CSI2RX config VIDEO_CADENCE_CSI2TX tristate "Cadence MIPI-CSI2 TX Controller" + depends on VIDEO_V4L2 depends on MEDIA_CONTROLLER depends on VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE -- cgit v1.2.3 From 4e61d7d1dd4c5b2e6da73852c24160e2ce7f8147 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 May 2018 06:51:23 -0400 Subject: media: v4l: rcar_fdp1: Change platform dependency to ARCH_RENESAS The Renesas Fine Display Processor driver is used on Renesas R-Car SoCs only. Since commit 9b5ba0df4ea4f940 ("ARM: shmobile: Introduce ARCH_RENESAS") is ARCH_RENESAS a more appropriate platform dependency than the legacy ARCH_SHMOBILE, hence use the former. This will allow to drop ARCH_SHMOBILE on ARM and ARM64 in the near future. Signed-off-by: Geert Uytterhoeven Acked-by: Arnd Bergmann Acked-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 2728376b04b5..210b44a457eb 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -384,7 +384,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_FDP1 tristate "Renesas Fine Display Processor" depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST depends on (!ARCH_RENESAS && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV -- cgit v1.2.3 From 9fab166a3e8f9e071de3418d27ffd03cdba2e9df Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 11:25:10 -0400 Subject: media: cx231xx: fix RC_CORE dependency With CONFIG_RC_CORE=m and VIDEO_CX231XX=y, we get a link failure: drivers/media/usb/cx231xx/cx231xx-input.o: In function `cx231xx_ir_init': cx231xx-input.c:(.text+0xd4): undefined reference to `rc_allocate_device' This narrows down the dependency so that only valid configurations are allowed. Fixes: 84545d2a1436 ("media: cx231xx: Remove RC_CORE dependency") Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 0f13192634c7..9e5b3e7c3ef5 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -15,7 +15,7 @@ config VIDEO_CX231XX config VIDEO_CX231XX_RC bool "Conexant cx231xx Remote Controller additional support" - depends on RC_CORE + depends on RC_CORE=y || RC_CORE=VIDEO_CX231XX depends on VIDEO_CX231XX default y ---help--- -- cgit v1.2.3 From 64bac6916ef7d9cc57367893aea1544fcad91b9b Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 25 May 2018 17:54:00 -0400 Subject: media: tc358743: release device_node in tc358743_probe_of() of_graph_get_next_endpoint() returns device_node with refcnt increased, but these is no of_node_put() for it. The patch adds one on error and normal paths. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Reviewed-by: Nicholas Mc Guire Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 393bbbbbaad7..44c41933415a 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1918,7 +1918,8 @@ static int tc358743_probe_of(struct tc358743_state *state) endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); if (IS_ERR(endpoint)) { dev_err(dev, "failed to parse endpoint\n"); - return PTR_ERR(endpoint); + ret = PTR_ERR(endpoint); + goto put_node; } if (endpoint->bus_type != V4L2_MBUS_CSI2 || @@ -2013,6 +2014,8 @@ disable_clk: clk_disable_unprepare(refclk); free_endpoint: v4l2_fwnode_endpoint_free(endpoint); +put_node: + of_node_put(ep); return ret; } #else -- cgit v1.2.3 From 42f073d7656bcfe6915b7023cb01427caa29a9be Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 26 May 2018 09:45:07 -0400 Subject: media: mtk-vpu: fix spelling mistake: "Prosessor" -> "Processor" Trivial fix to spelling mistake in module description text. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vpu/mtk_vpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index 1ff6a93262b7..f8d35e3ac1dc 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -960,4 +960,4 @@ static struct platform_driver mtk_vpu_driver = { module_platform_driver(mtk_vpu_driver); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver"); +MODULE_DESCRIPTION("Mediatek Video Processor Unit driver"); -- cgit v1.2.3 From 854dca5053d3653394aad319e3bd70c245401f12 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 26 May 2018 10:33:55 -0400 Subject: media: dt: bindings: tegra-vde: Document new optional Memory Client reset property Recently binding of the Memory Controller has been extended, exposing the Memory Client reset controls and hence it is now a reset controller. Tegra video-decoder device is among the Memory Controller reset users, document the new optional VDE HW reset property. Signed-off-by: Dmitry Osipenko Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt index 470237ed6fe5..7302e949e662 100644 --- a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt +++ b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt @@ -27,9 +27,15 @@ Required properties: - sxe - clocks : Must include the following entries: - vde -- resets : Must include the following entries: +- resets : Must contain an entry for each entry in reset-names. +- reset-names : Should include the following entries: - vde +Optional properties: +- resets : Must contain an entry for each entry in reset-names. +- reset-names : Must include the following entries: + - mc + Example: video-codec@6001a000 { @@ -51,5 +57,6 @@ video-codec@6001a000 { ; /* SXE interrupt */ interrupt-names = "sync-token", "bsev", "sxe"; clocks = <&tegra_car TEGRA20_CLK_VDE>; - resets = <&tegra_car 61>; + reset-names = "vde", "mc"; + resets = <&tegra_car 61>, <&mc TEGRA20_MC_RESET_VDE>; }; -- cgit v1.2.3 From 5a0996af33879c027725daa58becdf172e0f166d Mon Sep 17 00:00:00 2001 From: Janani Sankara Babu Date: Tue, 29 May 2018 19:08:22 -0400 Subject: media: Staging:media:imx Fix multiple assignments in a line This patch solves multiple assignments warning shown by checkpatch script. Signed-off-by: Janani Sankara Babu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 95d7805f3485..06af76d31c0b 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1192,10 +1192,12 @@ static int csi_enum_frame_size(struct v4l2_subdev *sd, } else { crop = __csi_get_crop(priv, cfg, fse->which); - fse->min_width = fse->max_width = fse->index & 1 ? + fse->min_width = fse->index & 1 ? crop->width / 2 : crop->width; - fse->min_height = fse->max_height = fse->index & 2 ? + fse->max_width = fse->min_width; + fse->min_height = fse->index & 2 ? crop->height / 2 : crop->height; + fse->max_height = fse->min_height; } mutex_unlock(&priv->lock); -- cgit v1.2.3 From 7fc77a2fdaa985393d84b8967db2c27664ec384b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 May 2018 18:07:11 -0400 Subject: media: v4l: omap: add VIDEO_V4L2 dependency The omap media driver can be built-in while the v4l2 core is a loadable module. This is a mistake and leads to link errors: drivers/media/platform/omap/omap_vout.o: In function `omap_vout_remove': omap_vout.c:(.text+0xec): undefined reference to `v4l2_device_unregister' omap_vout.c:(.text+0x140): undefined reference to `video_device_release' omap_vout.c:(.text+0x150): undefined reference to `video_unregister_device' omap_vout.c:(.text+0x15c): undefined reference to `v4l2_ctrl_handler_free' An explicit Kconfig dependency on VIDEO_V4L2 avoids the problem. I ran into this problem for the first time today during my randconfig builds, but could not find what caused it. Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index d827b6c285a6..4b5e55d41ad4 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -8,6 +8,7 @@ config VIDEO_OMAP2_VOUT depends on MMU depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n) depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST + depends on VIDEO_V4L2 select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 -- cgit v1.2.3 From e32eb0d8a6cdc965347b964e45e40c9254bd7408 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 1 Jun 2018 05:21:32 -0400 Subject: media: adv7604: simplify of_node_put() As the of_node_put() is unconditional here there is no need to have it twice. Signed-off-by: Nicholas Mc Guire Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index cac2081e876e..1a3b2c04d9f9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3108,12 +3108,9 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg); - if (ret) { - of_node_put(endpoint); - return ret; - } - of_node_put(endpoint); + if (ret) + return ret; if (!of_property_read_u32(np, "default-input", &v)) state->pdata.default_input = v; -- cgit v1.2.3 From e8ced209510164f0b54d4bb2a34d243a8af8f561 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 1 Jun 2018 08:46:14 -0400 Subject: media: atmel-isi: drop unnecessary while loop As there is no way this can loop it actually makes no sense to have a while(1){} around the body - all three possible paths end in a return statement. Fixes: commit c1d82b895380 "[media] atmel-isi: move out of soc_camera to atmel" Signed-off-by: Nicholas Mc Guire Acked-by: Ludovic Desroches Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isi.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index e5be21a31640..85fc7b98b660 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1106,23 +1106,21 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) struct device_node *ep = NULL; struct device_node *remote; - while (1) { - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; - - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - of_node_put(ep); - return -EINVAL; - } + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + return -EINVAL; - /* Remote node to connect */ - isi->entity.node = remote; - isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - isi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + of_node_put(ep); + return -EINVAL; } + + /* Remote node to connect */ + isi->entity.node = remote; + isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + isi->entity.asd.match.fwnode = of_fwnode_handle(remote); + return 0; } static int isi_graph_init(struct atmel_isi *isi) -- cgit v1.2.3 From 4f9195e3f88c13de94f5828f53d0ffab00d20e6d Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 1 Jun 2018 09:30:14 -0400 Subject: media: atmel-isi: move of_node_put() to cover success branch as well The of_node_put() was only covering the error branch but missed the success branch so the refcount for ep which of_graph_get_remote_port_parent() incremented on success would was not being decremented. Signed-off-by: Nicholas Mc Guire Acked-by: Ludovic Desroches Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 85fc7b98b660..e8db4df1e7c4 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1111,10 +1111,9 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) return -EINVAL; remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - of_node_put(ep); + of_node_put(ep); + if (!remote) return -EINVAL; - } /* Remote node to connect */ isi->entity.node = remote; -- cgit v1.2.3 From 055eceebecfe0d0997b6f7902b36cd6bb5fac860 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 6 Jun 2018 09:21:53 -0400 Subject: media: renesas-ceu: Add support for YUYV permutations Add support for YUYV permutations, passing down to the subdevice the media bus format with components ordering that reflect the output pixel format one. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas-ceu.c | 91 ++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index fe4fe944592d..ad782901cd7a 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -254,6 +254,18 @@ static const struct ceu_fmt ceu_fmt_list[] = { .fourcc = V4L2_PIX_FMT_YUYV, .bpp = 16, }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .bpp = 16, + }, }; static const struct ceu_fmt *get_ceu_fmt_from_fourcc(unsigned int fourcc) @@ -272,6 +284,9 @@ static bool ceu_fmt_mplane(struct v4l2_pix_format_mplane *pix) { switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: return false; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: @@ -380,7 +395,9 @@ static int ceu_hw_config(struct ceu_device *ceudev) switch (pix->pixelformat) { /* Data fetch sync mode */ case V4L2_PIX_FMT_YUYV: - /* TODO: handle YUYV permutations through DTARY bits. */ + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: camcr = CEU_CAMCR_JPEG; cdocr |= CEU_CDOCR_NO_DOWSAMPLE; cfzsr = (pix->height << 16) | pix->width; @@ -568,6 +585,9 @@ static void ceu_calc_plane_sizes(struct ceu_device *ceudev, switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: pix->num_planes = 1; bpl = pix->width * ceu_fmt->bpp / 8; szimage = pix->height * bpl; @@ -762,42 +782,59 @@ static const struct vb2_ops ceu_vb2_ops = { /* --- CEU image formats handling --- */ /* - * ceu_try_fmt() - test format on CEU and sensor + * __ceu_try_fmt() - test format on CEU and sensor * @ceudev: The CEU device. * @v4l2_fmt: format to test. + * @sd_mbus_code: the media bus code accepted by the subdevice; output param. * * Returns 0 for success, < 0 for errors. */ -static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +static int __ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt, + u32 *sd_mbus_code) { struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; struct v4l2_subdev_pad_config pad_cfg; const struct ceu_fmt *ceu_fmt; + u32 mbus_code_old; + u32 mbus_code; int ret; /* * Set format on sensor sub device: bus format used to produce memory - * format is selected at initialization time. + * format is selected depending on YUV component ordering or + * at initialization time. */ struct v4l2_subdev_format sd_format = { .which = V4L2_SUBDEV_FORMAT_TRY, - .format = { - .code = ceu_sd->mbus_fmt.mbus_code, - }, }; + mbus_code_old = ceu_sd->mbus_fmt.mbus_code; + switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + mbus_code = MEDIA_BUS_FMT_YUYV8_2X8; + break; + case V4L2_PIX_FMT_UYVY: + mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; + break; + case V4L2_PIX_FMT_YVYU: + mbus_code = MEDIA_BUS_FMT_YVYU8_2X8; + break; + case V4L2_PIX_FMT_VYUY: + mbus_code = MEDIA_BUS_FMT_VYUY8_2X8; + break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: + mbus_code = ceu_sd->mbus_fmt.mbus_code; break; default: pix->pixelformat = V4L2_PIX_FMT_NV16; + mbus_code = ceu_sd->mbus_fmt.mbus_code; break; } @@ -808,9 +845,25 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) &pix->height, 4, CEU_MAX_HEIGHT, 4, 0); v4l2_fill_mbus_format_mplane(&sd_format.format, pix); + + /* + * Try with the mbus_code matching YUYV components ordering first, + * if that one fails, fallback to default selected at initialization + * time. + */ + sd_format.format.code = mbus_code; ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format); - if (ret) - return ret; + if (ret) { + if (ret == -EINVAL) { + /* fallback */ + sd_format.format.code = mbus_code_old; + ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, + &pad_cfg, &sd_format); + } + + if (ret) + return ret; + } /* Apply size returned by sensor as the CEU can't scale. */ v4l2_fill_pix_format_mplane(pix, &sd_format.format); @@ -818,9 +871,22 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) /* Calculate per-plane sizes based on image format. */ ceu_calc_plane_sizes(ceudev, ceu_fmt, pix); + /* Report to caller the configured mbus format. */ + *sd_mbus_code = sd_format.format.code; + return 0; } +/* + * ceu_try_fmt() - Wrapper for __ceu_try_fmt; discard configured mbus_fmt + */ +static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +{ + u32 mbus_code; + + return __ceu_try_fmt(ceudev, v4l2_fmt, &mbus_code); +} + /* * ceu_set_fmt() - Apply the supplied format to both sensor and CEU */ @@ -828,6 +894,7 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) { struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + u32 mbus_code; int ret; /* @@ -836,15 +903,13 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) */ struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = ceu_sd->mbus_fmt.mbus_code, - }, }; - ret = ceu_try_fmt(ceudev, v4l2_fmt); + ret = __ceu_try_fmt(ceudev, v4l2_fmt, &mbus_code); if (ret) return ret; + format.format.code = mbus_code; v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp); ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format); if (ret) -- cgit v1.2.3 From ce3ff60f02349471b3bb24516b87da799a5b61e6 Mon Sep 17 00:00:00 2001 From: Gabriel Fanelli Date: Thu, 7 Jun 2018 03:12:16 -0400 Subject: media: staging: media: bcm2048: match alignment with open parenthesis This patch fixes the checkpatch.pl issue: CHECK: Alignment should match open parenthesis Signed-off-by: Gabriel Fanelli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/bcm2048/radio-bcm2048.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 06d1920150da..a90b2eb112f9 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2175,7 +2175,7 @@ static int bcm2048_fops_release(struct file *file) } static __poll_t bcm2048_fops_poll(struct file *file, - struct poll_table_struct *pts) + struct poll_table_struct *pts) { struct bcm2048_device *bdev = video_drvdata(file); __poll_t retval = 0; -- cgit v1.2.3 From 653d500ccaadd76ccade9f07469cdc66759315b6 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sun, 6 May 2018 04:06:07 -0400 Subject: media: i2c: lm3560: add support for lm3559 chip Add support for LM3559, as found in Motorola Droid 4 phone, for example. SW interface seems to be identical. Signed-off-by: Pavel Machek Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/lm3560.c | 3 ++- include/media/i2c/lm3560.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index b600e03aa94b..49c6644cbba7 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -1,6 +1,6 @@ /* * drivers/media/i2c/lm3560.c - * General device driver for TI lm3560, FLASH LED Driver + * General device driver for TI lm3559, lm3560, FLASH LED Driver * * Copyright (C) 2013 Texas Instruments * @@ -465,6 +465,7 @@ static int lm3560_remove(struct i2c_client *client) } static const struct i2c_device_id lm3560_id_table[] = { + {LM3559_NAME, 0}, {LM3560_NAME, 0}, {} }; diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h index a5bd310c9e1e..0e2b1c751a5d 100644 --- a/include/media/i2c/lm3560.h +++ b/include/media/i2c/lm3560.h @@ -22,6 +22,7 @@ #include +#define LM3559_NAME "lm3559" #define LM3560_NAME "lm3560" #define LM3560_I2C_ADDR (0x53) -- cgit v1.2.3 From fd59ccb9b6cb5051b84ec7ac9df32631ec43da2a Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 9 May 2018 14:45:58 -0400 Subject: media: rcar-vin: Drop unnecessary register properties from example vin port The example vin port node does not have an address and thus does not need address-cells or address size-properties. Signed-off-by: Simon Horman Acked-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index a19517e1c669..2a0c59e97f40 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -107,9 +107,6 @@ Board setup example for Gen2 platforms (vin1 composite video input) status = "okay"; port { - #address-cells = <1>; - #size-cells = <0>; - vin1ep0: endpoint { remote-endpoint = <&adv7180>; bus-width = <8>; -- cgit v1.2.3 From 6ad48c27fc08e1cc931798399eccfdc46b45f4b8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Sun, 13 May 2018 14:58:18 -0400 Subject: media: dt-bindings: media: rcar_vin: add support for r8a77965 Signed-off-by: Niklas Söderlund Reviewed-by: Simon Horman Acked-by: Rob Herring Reviewed-by: Simon Horman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 2a0c59e97f40..c65e638f0f24 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -21,6 +21,7 @@ on Gen3 platforms to a CSI-2 receiver. - "renesas,vin-r8a7794" for the R8A7794 device - "renesas,vin-r8a7795" for the R8A7795 device - "renesas,vin-r8a7796" for the R8A7796 device + - "renesas,vin-r8a77965" for the R8A77965 device - "renesas,vin-r8a77970" for the R8A77970 device - "renesas,rcar-gen2-vin" for a generic R-Car Gen2 or RZ/G1 compatible device. -- cgit v1.2.3 From e10cf92ead486b24a3169bb3b3215cf5775225fd Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 28 May 2018 19:17:04 -0400 Subject: media: dt-bindings: media: rcar_vin: fix style for ports and endpoints The style for referring to ports and endpoint are wrong. Refer to them using lowercase and a unit address, port@x and endpoint@x. Signed-off-by: Niklas Söderlund Reported-by: Geert Uytterhoeven Reviewed-by: Rob Herring Reviewed-by: Simon Horman Reviewed-by: Simon Horman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index c65e638f0f24..638cecdde474 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -45,23 +45,23 @@ The per-board settings Gen2 platforms: The per-board settings Gen3 platforms: Gen3 platforms can support both a single connected parallel input source -from external SoC pins (port0) and/or multiple parallel input sources -from local SoC CSI-2 receivers (port1) depending on SoC. +from external SoC pins (port@0) and/or multiple parallel input sources +from local SoC CSI-2 receivers (port@1) depending on SoC. - renesas,id - ID number of the VIN, VINx in the documentation. - ports - - port 0 - sub-node describing a single endpoint connected to the VIN + - port@0 - sub-node describing a single endpoint connected to the VIN from external SoC pins described in video-interfaces.txt[1]. - Describing more then one endpoint in port 0 is invalid. Only VIN - instances that are connected to external pins should have port 0. - - port 1 - sub-nodes describing one or more endpoints connected to + Describing more than one endpoint in port@0 is invalid. Only VIN + instances that are connected to external pins should have port@0. + - port@1 - sub-nodes describing one or more endpoints connected to the VIN from local SoC CSI-2 receivers. The endpoint numbers must use the following schema. - - Endpoint 0 - sub-node describing the endpoint connected to CSI20 - - Endpoint 1 - sub-node describing the endpoint connected to CSI21 - - Endpoint 2 - sub-node describing the endpoint connected to CSI40 - - Endpoint 3 - sub-node describing the endpoint connected to CSI41 + - endpoint@0 - sub-node describing the endpoint connected to CSI20 + - endpoint@1 - sub-node describing the endpoint connected to CSI21 + - endpoint@2 - sub-node describing the endpoint connected to CSI40 + - endpoint@3 - sub-node describing the endpoint connected to CSI41 Device node example for Gen2 platforms -------------------------------------- -- cgit v1.2.3 From 23689ab1add36964b69b6e4fdb97d52f315119f5 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 28 May 2018 19:26:37 -0400 Subject: media: rcar-vin: sync which hardware buffer to start capture from When starting the VIN capture procedure we are not guaranteed that the first buffer written to is VnMB1 to which we assigned the first buffer queued. This is problematic for two reasons. Buffers might not be dequeued in the same order they where queued for capture. Future features planed for the VIN driver is support for outputting frames in SEQ_TB/BT format and to do that it's important that capture starts from the first buffer slot, VnMB1. We are guaranteed that capturing always happens in sequence (VnMB1 -> VnMB2 -> VnMB3 -> VnMB1). So drop up to two frames when starting capturing so that the driver always returns buffers in the same order they are queued and prepare for SEQ_TB/BT output. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 16 +++++++++++++++- drivers/media/platform/rcar-vin/rcar-vin.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index ac07f99e3516..cfe5d7a9d44e 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -856,7 +856,7 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = RUNNING; + vin->state = STARTING; return 0; } @@ -910,6 +910,20 @@ static irqreturn_t rvin_irq(int irq, void *data) vnms = rvin_read(vin, VNMS_REG); slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (vin->state == STARTING) { + if (slot != 0) { + vin_dbg(vin, "Starting sync slot: %d\n", slot); + goto done; + } + + vin_dbg(vin, "Capture start synced!\n"); + vin->state = RUNNING; + } + /* Capture frame */ if (vin->queue_buf[slot]) { vin->queue_buf[slot]->field = vin->format.field; diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index c2aef789b491..ff747e22d8cf 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -53,11 +53,13 @@ enum rvin_csi_id { /** * STOPPED - No operation in progress + * STARTING - Capture starting up * RUNNING - Operation in progress have buffers * STOPPING - Stopping operation */ enum rvin_dma_state { STOPPED = 0, + STARTING, RUNNING, STOPPING, }; -- cgit v1.2.3 From a740e3b2f7a26c5dd13741d399b69e22660b1b96 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 28 May 2018 19:49:06 -0400 Subject: media: rcar-vin: enable support for r8a77965 Add the SoC specific information for Renesas r8a77965. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index d3072e166a1c..e6a010ee4ba1 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -974,6 +974,50 @@ static const struct rvin_info rcar_info_r8a7796 = { .routes = rcar_info_r8a7796_routes, }; +static const struct rvin_group_route rcar_info_r8a77965_routes[] = { + { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77965 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77965_routes, +}; + static const struct rvin_group_route _rcar_info_r8a77970_routes[] = { { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, @@ -1030,6 +1074,10 @@ static const struct of_device_id rvin_of_id_table[] = { .compatible = "renesas,vin-r8a7796", .data = &rcar_info_r8a7796, }, + { + .compatible = "renesas,vin-r8a77965", + .data = &rcar_info_r8a77965, + }, { .compatible = "renesas,vin-r8a77970", .data = &rcar_info_r8a77970, -- cgit v1.2.3 From f187352dcd45d6f48c43c78dcc0d88d5508ecb11 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 28 May 2018 12:37:07 -0400 Subject: media: i2c: Copy rj54n1cb0c soc_camera sensor driver Copy the soc_camera based driver in v4l2 sensor driver directory. This commit just copies the original file without modifying it. No modification to KConfig and Makefile as soc_camera framework dependencies need to be removed first in next commit. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/rj54n1cb0c.c | 1416 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1416 insertions(+) create mode 100644 drivers/media/i2c/rj54n1cb0c.c diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c new file mode 100644 index 000000000000..02398d0bc649 --- /dev/null +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -0,0 +1,1416 @@ +/* + * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp + * + * Copyright (C) 2009, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RJ54N1_DEV_CODE 0x0400 +#define RJ54N1_DEV_CODE2 0x0401 +#define RJ54N1_OUT_SEL 0x0403 +#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 +#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 +#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 +#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 +#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 +#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 +#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a +#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b +#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c +#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d +#define RJ54N1_RESIZE_N 0x040e +#define RJ54N1_RESIZE_N_STEP 0x040f +#define RJ54N1_RESIZE_STEP 0x0410 +#define RJ54N1_RESIZE_HOLD_H 0x0411 +#define RJ54N1_RESIZE_HOLD_L 0x0412 +#define RJ54N1_H_OBEN_OFS 0x0413 +#define RJ54N1_V_OBEN_OFS 0x0414 +#define RJ54N1_RESIZE_CONTROL 0x0415 +#define RJ54N1_STILL_CONTROL 0x0417 +#define RJ54N1_INC_USE_SEL_H 0x0425 +#define RJ54N1_INC_USE_SEL_L 0x0426 +#define RJ54N1_MIRROR_STILL_MODE 0x0427 +#define RJ54N1_INIT_START 0x0428 +#define RJ54N1_SCALE_1_2_LEV 0x0429 +#define RJ54N1_SCALE_4_LEV 0x042a +#define RJ54N1_Y_GAIN 0x04d8 +#define RJ54N1_APT_GAIN_UP 0x04fa +#define RJ54N1_RA_SEL_UL 0x0530 +#define RJ54N1_BYTE_SWAP 0x0531 +#define RJ54N1_OUT_SIGPO 0x053b +#define RJ54N1_WB_SEL_WEIGHT_I 0x054e +#define RJ54N1_BIT8_WB 0x0569 +#define RJ54N1_HCAPS_WB 0x056a +#define RJ54N1_VCAPS_WB 0x056b +#define RJ54N1_HCAPE_WB 0x056c +#define RJ54N1_VCAPE_WB 0x056d +#define RJ54N1_EXPOSURE_CONTROL 0x058c +#define RJ54N1_FRAME_LENGTH_S_H 0x0595 +#define RJ54N1_FRAME_LENGTH_S_L 0x0596 +#define RJ54N1_FRAME_LENGTH_P_H 0x0597 +#define RJ54N1_FRAME_LENGTH_P_L 0x0598 +#define RJ54N1_PEAK_H 0x05b7 +#define RJ54N1_PEAK_50 0x05b8 +#define RJ54N1_PEAK_60 0x05b9 +#define RJ54N1_PEAK_DIFF 0x05ba +#define RJ54N1_IOC 0x05ef +#define RJ54N1_TG_BYPASS 0x0700 +#define RJ54N1_PLL_L 0x0701 +#define RJ54N1_PLL_N 0x0702 +#define RJ54N1_PLL_EN 0x0704 +#define RJ54N1_RATIO_TG 0x0706 +#define RJ54N1_RATIO_T 0x0707 +#define RJ54N1_RATIO_R 0x0708 +#define RJ54N1_RAMP_TGCLK_EN 0x0709 +#define RJ54N1_OCLK_DSP 0x0710 +#define RJ54N1_RATIO_OP 0x0711 +#define RJ54N1_RATIO_O 0x0712 +#define RJ54N1_OCLK_SEL_EN 0x0713 +#define RJ54N1_CLK_RST 0x0717 +#define RJ54N1_RESET_STANDBY 0x0718 +#define RJ54N1_FWFLG 0x07fe + +#define E_EXCLK (1 << 7) +#define SOFT_STDBY (1 << 4) +#define SEN_RSTX (1 << 2) +#define TG_RSTX (1 << 1) +#define DSP_RSTX (1 << 0) + +#define RESIZE_HOLD_SEL (1 << 2) +#define RESIZE_GO (1 << 1) + +/* + * When cropping, the camera automatically centers the cropped region, there + * doesn't seem to be a way to specify an explicit location of the rectangle. + */ +#define RJ54N1_COLUMN_SKIP 0 +#define RJ54N1_ROW_SKIP 0 +#define RJ54N1_MAX_WIDTH 1600 +#define RJ54N1_MAX_HEIGHT 1200 + +#define PLL_L 2 +#define PLL_N 0x31 + +/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ + +/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ +struct rj54n1_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct rj54n1_datafmt *rj54n1_find_datafmt( + u32 code, const struct rj54n1_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { + {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, +}; + +struct rj54n1_clock_div { + u8 ratio_tg; /* can be 0 or an odd number */ + u8 ratio_t; + u8 ratio_r; + u8 ratio_op; + u8 ratio_o; +}; + +struct rj54n1 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_clk *clk; + struct rj54n1_clock_div clk_div; + const struct rj54n1_datafmt *fmt; + struct v4l2_rect rect; /* Sensor window */ + unsigned int tgclk_mhz; + bool auto_wb; + unsigned short width; /* Output window */ + unsigned short height; + unsigned short resize; /* Sensor * 1024 / resize = Output */ + unsigned short scale; + u8 bank; +}; + +struct rj54n1_reg_val { + u16 reg; + u8 val; +}; + +static const struct rj54n1_reg_val bank_4[] = { + {0x417, 0}, + {0x42c, 0}, + {0x42d, 0xf0}, + {0x42e, 0}, + {0x42f, 0x50}, + {0x430, 0xf5}, + {0x431, 0x16}, + {0x432, 0x20}, + {0x433, 0}, + {0x434, 0xc8}, + {0x43c, 8}, + {0x43e, 0x90}, + {0x445, 0x83}, + {0x4ba, 0x58}, + {0x4bb, 4}, + {0x4bc, 0x20}, + {0x4db, 4}, + {0x4fe, 2}, +}; + +static const struct rj54n1_reg_val bank_5[] = { + {0x514, 0}, + {0x516, 0}, + {0x518, 0}, + {0x51a, 0}, + {0x51d, 0xff}, + {0x56f, 0x28}, + {0x575, 0x40}, + {0x5bc, 0x48}, + {0x5c1, 6}, + {0x5e5, 0x11}, + {0x5e6, 0x43}, + {0x5e7, 0x33}, + {0x5e8, 0x21}, + {0x5e9, 0x30}, + {0x5ea, 0x0}, + {0x5eb, 0xa5}, + {0x5ec, 0xff}, + {0x5fe, 2}, +}; + +static const struct rj54n1_reg_val bank_7[] = { + {0x70a, 0}, + {0x714, 0xff}, + {0x715, 0xff}, + {0x716, 0x1f}, + {0x7FE, 2}, +}; + +static const struct rj54n1_reg_val bank_8[] = { + {0x800, 0x00}, + {0x801, 0x01}, + {0x802, 0x61}, + {0x805, 0x00}, + {0x806, 0x00}, + {0x807, 0x00}, + {0x808, 0x00}, + {0x809, 0x01}, + {0x80A, 0x61}, + {0x80B, 0x00}, + {0x80C, 0x01}, + {0x80D, 0x00}, + {0x80E, 0x00}, + {0x80F, 0x00}, + {0x810, 0x00}, + {0x811, 0x01}, + {0x812, 0x61}, + {0x813, 0x00}, + {0x814, 0x11}, + {0x815, 0x00}, + {0x816, 0x41}, + {0x817, 0x00}, + {0x818, 0x51}, + {0x819, 0x01}, + {0x81A, 0x1F}, + {0x81B, 0x00}, + {0x81C, 0x01}, + {0x81D, 0x00}, + {0x81E, 0x11}, + {0x81F, 0x00}, + {0x820, 0x41}, + {0x821, 0x00}, + {0x822, 0x51}, + {0x823, 0x00}, + {0x824, 0x00}, + {0x825, 0x00}, + {0x826, 0x47}, + {0x827, 0x01}, + {0x828, 0x4F}, + {0x829, 0x00}, + {0x82A, 0x00}, + {0x82B, 0x00}, + {0x82C, 0x30}, + {0x82D, 0x00}, + {0x82E, 0x40}, + {0x82F, 0x00}, + {0x830, 0xB3}, + {0x831, 0x00}, + {0x832, 0xE3}, + {0x833, 0x00}, + {0x834, 0x00}, + {0x835, 0x00}, + {0x836, 0x00}, + {0x837, 0x00}, + {0x838, 0x00}, + {0x839, 0x01}, + {0x83A, 0x61}, + {0x83B, 0x00}, + {0x83C, 0x01}, + {0x83D, 0x00}, + {0x83E, 0x00}, + {0x83F, 0x00}, + {0x840, 0x00}, + {0x841, 0x01}, + {0x842, 0x61}, + {0x843, 0x00}, + {0x844, 0x1D}, + {0x845, 0x00}, + {0x846, 0x00}, + {0x847, 0x00}, + {0x848, 0x00}, + {0x849, 0x01}, + {0x84A, 0x1F}, + {0x84B, 0x00}, + {0x84C, 0x05}, + {0x84D, 0x00}, + {0x84E, 0x19}, + {0x84F, 0x01}, + {0x850, 0x21}, + {0x851, 0x01}, + {0x852, 0x5D}, + {0x853, 0x00}, + {0x854, 0x00}, + {0x855, 0x00}, + {0x856, 0x19}, + {0x857, 0x01}, + {0x858, 0x21}, + {0x859, 0x00}, + {0x85A, 0x00}, + {0x85B, 0x00}, + {0x85C, 0x00}, + {0x85D, 0x00}, + {0x85E, 0x00}, + {0x85F, 0x00}, + {0x860, 0xB3}, + {0x861, 0x00}, + {0x862, 0xE3}, + {0x863, 0x00}, + {0x864, 0x00}, + {0x865, 0x00}, + {0x866, 0x00}, + {0x867, 0x00}, + {0x868, 0x00}, + {0x869, 0xE2}, + {0x86A, 0x00}, + {0x86B, 0x01}, + {0x86C, 0x06}, + {0x86D, 0x00}, + {0x86E, 0x00}, + {0x86F, 0x00}, + {0x870, 0x60}, + {0x871, 0x8C}, + {0x872, 0x10}, + {0x873, 0x00}, + {0x874, 0xE0}, + {0x875, 0x00}, + {0x876, 0x27}, + {0x877, 0x01}, + {0x878, 0x00}, + {0x879, 0x00}, + {0x87A, 0x00}, + {0x87B, 0x03}, + {0x87C, 0x00}, + {0x87D, 0x00}, + {0x87E, 0x00}, + {0x87F, 0x00}, + {0x880, 0x00}, + {0x881, 0x00}, + {0x882, 0x00}, + {0x883, 0x00}, + {0x884, 0x00}, + {0x885, 0x00}, + {0x886, 0xF8}, + {0x887, 0x00}, + {0x888, 0x03}, + {0x889, 0x00}, + {0x88A, 0x64}, + {0x88B, 0x00}, + {0x88C, 0x03}, + {0x88D, 0x00}, + {0x88E, 0xB1}, + {0x88F, 0x00}, + {0x890, 0x03}, + {0x891, 0x01}, + {0x892, 0x1D}, + {0x893, 0x00}, + {0x894, 0x03}, + {0x895, 0x01}, + {0x896, 0x4B}, + {0x897, 0x00}, + {0x898, 0xE5}, + {0x899, 0x00}, + {0x89A, 0x01}, + {0x89B, 0x00}, + {0x89C, 0x01}, + {0x89D, 0x04}, + {0x89E, 0xC8}, + {0x89F, 0x00}, + {0x8A0, 0x01}, + {0x8A1, 0x01}, + {0x8A2, 0x61}, + {0x8A3, 0x00}, + {0x8A4, 0x01}, + {0x8A5, 0x00}, + {0x8A6, 0x00}, + {0x8A7, 0x00}, + {0x8A8, 0x00}, + {0x8A9, 0x00}, + {0x8AA, 0x7F}, + {0x8AB, 0x03}, + {0x8AC, 0x00}, + {0x8AD, 0x00}, + {0x8AE, 0x00}, + {0x8AF, 0x00}, + {0x8B0, 0x00}, + {0x8B1, 0x00}, + {0x8B6, 0x00}, + {0x8B7, 0x01}, + {0x8B8, 0x00}, + {0x8B9, 0x00}, + {0x8BA, 0x02}, + {0x8BB, 0x00}, + {0x8BC, 0xFF}, + {0x8BD, 0x00}, + {0x8FE, 2}, +}; + +static const struct rj54n1_reg_val bank_10[] = { + {0x10bf, 0x69} +}; + +/* Clock dividers - these are default register values, divider = register + 1 */ +static const struct rj54n1_clock_div clk_div = { + .ratio_tg = 3 /* default: 5 */, + .ratio_t = 4 /* default: 1 */, + .ratio_r = 4 /* default: 0 */, + .ratio_op = 1 /* default: 5 */, + .ratio_o = 9 /* default: 0 */, +}; + +static struct rj54n1 *to_rj54n1(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); +} + +static int reg_read(struct i2c_client *client, const u16 reg) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + return i2c_smbus_read_byte_data(client, reg & 0xff); +} + +static int reg_write(struct i2c_client *client, const u16 reg, + const u8 data) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); + return i2c_smbus_write_byte_data(client, reg & 0xff, data); +} + +static int reg_set(struct i2c_client *client, const u16 reg, + const u8 data, const u8 mask) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static int reg_write_multiple(struct i2c_client *client, + const struct rj54n1_reg_val *rv, const int n) +{ + int i, ret; + + for (i = 0; i < n; i++) { + ret = reg_write(client, rv->reg, rv->val); + if (ret < 0) + return ret; + rv++; + } + + return 0; +} + +static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) + return -EINVAL; + + code->code = rj54n1_colour_fmts[code->index].code; + return 0; +} + +static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Switch between preview and still shot modes */ + return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); +} + +static int rj54n1_set_rect(struct i2c_client *client, + u16 reg_x, u16 reg_y, u16 reg_xy, + u32 width, u32 height) +{ + int ret; + + ret = reg_write(client, reg_xy, + ((width >> 4) & 0x70) | + ((height >> 8) & 7)); + + if (!ret) + ret = reg_write(client, reg_x, width & 0xff); + if (!ret) + ret = reg_write(client, reg_y, height & 0xff); + + return ret; +} + +/* + * Some commands, specifically certain initialisation sequences, require + * a commit operation. + */ +static int rj54n1_commit(struct i2c_client *client) +{ + int ret = reg_write(client, RJ54N1_INIT_START, 1); + msleep(10); + if (!ret) + ret = reg_write(client, RJ54N1_INIT_START, 0); + return ret; +} + +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h); + +static int rj54n1_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + const struct v4l2_rect *rect = &sel->r; + int dummy = 0, output_w, output_h, + input_w = rect->width, input_h = rect->height; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* arbitrary minimum width and height, edges unimportant */ + soc_camera_limit_side(&dummy, &input_w, + RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); + + soc_camera_limit_side(&dummy, &input_h, + RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT); + + output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; + output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; + + dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", + input_w, input_h, rj54n1->resize, output_w, output_h); + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + rj54n1->width = output_w; + rj54n1->height = output_h; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + + return 0; +} + +static int rj54n1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = RJ54N1_COLUMN_SKIP; + sel->r.top = RJ54N1_ROW_SKIP; + sel->r.width = RJ54N1_MAX_WIDTH; + sel->r.height = RJ54N1_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = rj54n1->rect; + return 0; + default: + return -EINVAL; + } +} + +static int rj54n1_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (format->pad) + return -EINVAL; + + mf->code = rj54n1->fmt->code; + mf->colorspace = rj54n1->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mf->width = rj54n1->width; + mf->height = rj54n1->height; + + return 0; +} + +/* + * The actual geometry configuration routine. It scales the input window into + * the output one, updates the window sizes and returns an error or the resize + * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. + */ +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + unsigned int skip, resize, input_w = *in_w, input_h = *in_h, + output_w = *out_w, output_h = *out_h; + u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; + unsigned int peak, peak_50, peak_60; + int ret; + + /* + * We have a problem with crops, where the window is larger than 512x384 + * and output window is larger than a half of the input one. In this + * case we have to either reduce the input window to equal or below + * 512x384 or the output window to equal or below 1/2 of the input. + */ + if (output_w > max(512U, input_w / 2)) { + if (2 * output_w > RJ54N1_MAX_WIDTH) { + input_w = RJ54N1_MAX_WIDTH; + output_w = RJ54N1_MAX_WIDTH / 2; + } else { + input_w = output_w * 2; + } + + dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", + input_w, output_w); + } + + if (output_h > max(384U, input_h / 2)) { + if (2 * output_h > RJ54N1_MAX_HEIGHT) { + input_h = RJ54N1_MAX_HEIGHT; + output_h = RJ54N1_MAX_HEIGHT / 2; + } else { + input_h = output_h * 2; + } + + dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", + input_h, output_h); + } + + /* Idea: use the read mode for snapshots, handle separate geometries */ + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, + RJ54N1_Y_OUTPUT_SIZE_S_L, + RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); + if (!ret) + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, + RJ54N1_Y_OUTPUT_SIZE_P_L, + RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); + + if (ret < 0) + return ret; + + if (output_w > input_w && output_h > input_h) { + input_w = output_w; + input_h = output_h; + + resize = 1024; + } else { + unsigned int resize_x, resize_y; + resize_x = (input_w * 1024 + output_w / 2) / output_w; + resize_y = (input_h * 1024 + output_h / 2) / output_h; + + /* We want max(resize_x, resize_y), check if it still fits */ + if (resize_x > resize_y && + (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) + resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / + output_h; + else if (resize_y > resize_x && + (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) + resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / + output_w; + else + resize = max(resize_x, resize_y); + + /* Prohibited value ranges */ + switch (resize) { + case 2040 ... 2047: + resize = 2039; + break; + case 4080 ... 4095: + resize = 4079; + break; + case 8160 ... 8191: + resize = 8159; + break; + case 16320 ... 16384: + resize = 16319; + } + } + + /* Set scaling */ + ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); + + if (ret < 0) + return ret; + + /* + * Configure a skipping bitmask. The sensor will select a skipping value + * among set bits automatically. This is very unclear in the datasheet + * too. I was told, in this register one enables all skipping values, + * that are required for a specific resize, and the camera selects + * automatically, which ones to use. But it is unclear how to identify, + * which cropping values are needed. Secondly, why don't we just set all + * bits and let the camera choose? Would it increase processing time and + * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to + * improve the image quality or stability for larger frames (see comment + * above), but I didn't check the framerate. + */ + skip = min(resize / 1024, 15U); + + inc_sel = 1 << skip; + + if (inc_sel <= 2) + inc_sel = 0xc; + else if (resize & 1023 && skip < 15) + inc_sel |= 1 << (skip + 1); + + ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); + if (!ret) + ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); + + if (!rj54n1->auto_wb) { + /* Auto white balance window */ + wb_left = output_w / 16; + wb_right = (3 * output_w / 4 - 3) / 4; + wb_top = output_h / 16; + wb_bottom = (3 * output_h / 4 - 3) / 4; + wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | + ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); + + if (!ret) + ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); + if (!ret) + ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); + if (!ret) + ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); + if (!ret) + ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); + if (!ret) + ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); + } + + /* Antiflicker */ + peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / + 10000; + peak_50 = peak / 6; + peak_60 = peak / 5; + + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_H, + ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_50, peak_50); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_60, peak_60); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); + + /* Start resizing */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | RESIZE_GO | 1); + + if (ret < 0) + return ret; + + /* Constant taken from manufacturer's example */ + msleep(230); + + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); + if (ret < 0) + return ret; + + *in_w = (output_w * resize + 512) / 1024; + *in_h = (output_h * resize + 512) / 1024; + *out_w = output_w; + *out_h = output_h; + + dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", + *in_w, *in_h, resize, output_w, output_h, skip); + + return resize; +} + +static int rj54n1_set_clock(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* Enable external clock */ + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); + /* Leave stand-by. Note: use this when implementing suspend / resume */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); + + if (!ret) + ret = reg_write(client, RJ54N1_PLL_L, PLL_L); + if (!ret) + ret = reg_write(client, RJ54N1_PLL_N, PLL_N); + + /* TGCLK dividers */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_TG, + rj54n1->clk_div.ratio_tg); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_T, + rj54n1->clk_div.ratio_t); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_R, + rj54n1->clk_div.ratio_r); + + /* Enable TGCLK & RAMP */ + if (!ret) + ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); + + /* Disable clock output */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_DSP, 0); + + /* Set divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_OP, + rj54n1->clk_div.ratio_op); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_O, + rj54n1->clk_div.ratio_o); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + /* Use PLL for Timing Generator, write 2 to reserved bits */ + if (!ret) + ret = reg_write(client, RJ54N1_TG_BYPASS, 2); + + /* Take sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | SEN_RSTX); + /* Enable PLL */ + if (!ret) + ret = reg_write(client, RJ54N1_PLL_EN, 1); + + /* Wait for PLL to stabilise */ + msleep(10); + + /* Enable clock to frequency divider */ + if (!ret) + ret = reg_write(client, RJ54N1_CLK_RST, 1); + + if (!ret) + ret = reg_read(client, RJ54N1_CLK_RST); + if (ret != 1) { + dev_err(&client->dev, + "Resetting RJ54N1CB0C clock failed: %d!\n", ret); + return -EIO; + } + + /* Start the PLL */ + ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + return ret; +} + +static int rj54n1_reg_init(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret = rj54n1_set_clock(client); + + if (!ret) + ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); + if (!ret) + ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); + + /* Set binning divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); + + /* Switch to fixed resize mode */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | 1); + + /* Set gain */ + if (!ret) + ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); + + /* + * Mirror the image back: default is upside down and left-to-right... + * Set manual preview / still shot switching + */ + if (!ret) + ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); + + if (!ret) + ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); + + /* Auto exposure area */ + if (!ret) + ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); + /* Check current auto WB config */ + if (!ret) + ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); + if (ret >= 0) { + rj54n1->auto_wb = ret & 0x80; + ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); + } + if (!ret) + ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); + + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | SEN_RSTX); + + /* Commit init */ + if (!ret) + ret = rj54n1_commit(client); + + /* Take DSP, TG, sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); + + /* Start register update? Same register as 0x?FE in many bank_* sets */ + if (!ret) + ret = reg_write(client, RJ54N1_FWFLG, 2); + + /* Constant taken from manufacturer's example */ + msleep(700); + + return ret; +} + +static int rj54n1_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + const struct rj54n1_datafmt *fmt; + int output_w, output_h, max_w, max_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; + int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; + int ret; + + if (format->pad) + return -EINVAL; + + dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", + __func__, mf->code, mf->width, mf->height); + + fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, + ARRAY_SIZE(rj54n1_colour_fmts)); + if (!fmt) { + fmt = rj54n1->fmt; + mf->code = fmt->code; + } + + mf->field = V4L2_FIELD_NONE; + mf->colorspace = fmt->colorspace; + + v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, + &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + /* + * Verify if the sensor has just been powered on. TODO: replace this + * with proper PM, when a suitable API is available. + */ + ret = reg_read(client, RJ54N1_RESET_STANDBY); + if (ret < 0) + return ret; + + if (!(ret & E_EXCLK)) { + ret = rj54n1_reg_init(client); + if (ret < 0) + return ret; + } + + /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ + switch (mf->code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + break; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case MEDIA_BUS_FMT_RGB565_2X8_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + ret = reg_write(client, RJ54N1_OUT_SEL, 5); + break; + default: + ret = -EINVAL; + } + + /* Special case: a raw mode with 10 bits of data per clock tick */ + if (!ret) + ret = reg_set(client, RJ54N1_OCLK_SEL_EN, + (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2); + + if (ret < 0) + return ret; + + /* Supported scales 1:1 >= scale > 1:16 */ + max_w = mf->width * (16 * 1024 - 1) / 1024; + if (input_w > max_w) + input_w = max_w; + max_h = mf->height * (16 * 1024 - 1) / 1024; + if (input_h > max_h) + input_h = max_h; + + output_w = mf->width; + output_h = mf->height; + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, + ARRAY_SIZE(rj54n1_colour_fmts)); + + rj54n1->fmt = fmt; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + rj54n1->width = output_w; + rj54n1->height = output_h; + + mf->width = output_w; + mf->height = output_h; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = fmt->colorspace; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int rj54n1_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers > 0x0800 are only available from Sharp support */ + return -EINVAL; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xff) + return -EIO; + + return 0; +} + +static int rj54n1_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers >= 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int rj54n1_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on); +} + +static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); + struct v4l2_subdev *sd = &rj54n1->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_HFLIP: + if (ctrl->val) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_GAIN: + if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) + return -EIO; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + /* Auto WB area - whole image */ + if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, + 0x80) < 0) + return -EIO; + rj54n1->auto_wb = ctrl->val; + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { + .s_ctrl = rj54n1_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = rj54n1_g_register, + .s_register = rj54n1_s_register, +#endif + .s_power = rj54n1_s_power, +}; + +static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ + if (soc_camera_apply_board_flags(ssdd, cfg) & + V4L2_MBUS_PCLK_SAMPLE_RISING) + return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); + else + return reg_write(client, RJ54N1_OUT_SIGPO, 0); +} + +static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { + .s_stream = rj54n1_s_stream, + .g_mbus_config = rj54n1_g_mbus_config, + .s_mbus_config = rj54n1_s_mbus_config, +}; + +static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { + .enum_mbus_code = rj54n1_enum_mbus_code, + .get_selection = rj54n1_get_selection, + .set_selection = rj54n1_set_selection, + .get_fmt = rj54n1_get_fmt, + .set_fmt = rj54n1_set_fmt, +}; + +static const struct v4l2_subdev_ops rj54n1_subdev_ops = { + .core = &rj54n1_subdev_core_ops, + .video = &rj54n1_subdev_video_ops, + .pad = &rj54n1_subdev_pad_ops, +}; + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int rj54n1_video_probe(struct i2c_client *client, + struct rj54n1_pdata *priv) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int data1, data2; + int ret; + + ret = rj54n1_s_power(&rj54n1->subdev, 1); + if (ret < 0) + return ret; + + /* Read out the chip version register */ + data1 = reg_read(client, RJ54N1_DEV_CODE); + data2 = reg_read(client, RJ54N1_DEV_CODE2); + + if (data1 != 0x51 || data2 != 0x10) { + ret = -ENODEV; + dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", + data1, data2); + goto done; + } + + /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ + ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); + if (ret < 0) + goto done; + + dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", + data1, data2); + + ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); + +done: + rj54n1_s_power(&rj54n1->subdev, 0); + return ret; +} + +static int rj54n1_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct rj54n1 *rj54n1; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rj54n1_pdata *rj54n1_priv; + int ret; + + if (!ssdd || !ssdd->drv_priv) { + dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); + return -EINVAL; + } + + rj54n1_priv = ssdd->drv_priv; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); + if (!rj54n1) + return -ENOMEM; + + v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + v4l2_ctrl_handler_init(&rj54n1->hdl, 4); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 66); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + rj54n1->subdev.ctrl_handler = &rj54n1->hdl; + if (rj54n1->hdl.error) + return rj54n1->hdl.error; + + rj54n1->clk_div = clk_div; + rj54n1->rect.left = RJ54N1_COLUMN_SKIP; + rj54n1->rect.top = RJ54N1_ROW_SKIP; + rj54n1->rect.width = RJ54N1_MAX_WIDTH; + rj54n1->rect.height = RJ54N1_MAX_HEIGHT; + rj54n1->width = RJ54N1_MAX_WIDTH; + rj54n1->height = RJ54N1_MAX_HEIGHT; + rj54n1->fmt = &rj54n1_colour_fmts[0]; + rj54n1->resize = 1024; + rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / + (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); + + rj54n1->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(rj54n1->clk)) { + ret = PTR_ERR(rj54n1->clk); + goto eclkget; + } + + ret = rj54n1_video_probe(client, rj54n1_priv); + if (ret < 0) { + v4l2_clk_put(rj54n1->clk); +eclkget: + v4l2_ctrl_handler_free(&rj54n1->hdl); + } + + return ret; +} + +static int rj54n1_remove(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + v4l2_clk_put(rj54n1->clk); + v4l2_device_unregister_subdev(&rj54n1->subdev); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + v4l2_ctrl_handler_free(&rj54n1->hdl); + + return 0; +} + +static const struct i2c_device_id rj54n1_id[] = { + { "rj54n1cb0c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rj54n1_id); + +static struct i2c_driver rj54n1_i2c_driver = { + .driver = { + .name = "rj54n1cb0c", + }, + .probe = rj54n1_probe, + .remove = rj54n1_remove, + .id_table = rj54n1_id, +}; + +module_i2c_driver(rj54n1_i2c_driver); + +MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8cba1ae675f2b4662a62e8c83a4a172360bdf3ea Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 28 May 2018 12:37:08 -0400 Subject: media: i2c: rj54n1: Remove soc_camera dependencies Remove soc_camera framework dependencies from rj54n1 sensor driver. - Handle clock - Handle GPIOs (named 'powerup' and 'enable') - Register the async subdevice - Remove g/s_mbus_config as they're deprecated. - Adjust build system - List the driver as maintained for 'Odd Fixes' as I don't have HW to test. This commits does not remove the original soc_camera based driver. Compiled tested only. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 +++ drivers/media/i2c/Kconfig | 11 +++ drivers/media/i2c/Makefile | 1 + drivers/media/i2c/rj54n1cb0c.c | 153 +++++++++++++++++++++++------------------ 4 files changed, 107 insertions(+), 66 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6cfd16790add..5f4b8d14e75d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12845,6 +12845,14 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: net/smc/ +SHARP RJ54N1CB0C SENSOR DRIVER +M: Jacopo Mondi +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Odd fixes +F: drivers/media/i2c/rj54n1cb0c.c +F: include/media/i2c/rj54n1cb0c.h + SH_VEU V4L2 MEM2MEM DRIVER L: linux-media@vger.kernel.org S: Orphan diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 341452fe98df..1d5efef77e77 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -857,6 +857,17 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_RJ54N1 + tristate "Sharp RJ54N1CB0C sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a V4L2 sensor-level driver for Sharp RJ54N1CB0C CMOS image + sensor. + + To compile this driver as a module, choose M here: the + module will be called rj54n1. + config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" depends on MEDIA_CAMERA_SUPPORT diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index d679d57cd3b3..33cf2a415657 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index 02398d0bc649..6ad998ad1b16 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1,25 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp * - * Copyright (C) 2009, Guennadi Liakhovetski + * Copyright (C) 2018, Jacopo Mondi * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * Copyright (C) 2009, Guennadi Liakhovetski */ +#include #include +#include #include +#include #include #include #include -#include #include -#include -#include -#include +#include #include +#include #define RJ54N1_DEV_CODE 0x0400 #define RJ54N1_DEV_CODE2 0x0401 @@ -151,7 +151,9 @@ struct rj54n1_clock_div { struct rj54n1 { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; + struct clk *clk; + struct gpio_desc *pwup_gpio; + struct gpio_desc *enable_gpio; struct rj54n1_clock_div clk_div; const struct rj54n1_datafmt *fmt; struct v4l2_rect rect; /* Sensor window */ @@ -545,8 +547,7 @@ static int rj54n1_set_selection(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); const struct v4l2_rect *rect = &sel->r; - int dummy = 0, output_w, output_h, - input_w = rect->width, input_h = rect->height; + int output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || @@ -554,11 +555,8 @@ static int rj54n1_set_selection(struct v4l2_subdev *sd, return -EINVAL; /* arbitrary minimum width and height, edges unimportant */ - soc_camera_limit_side(&dummy, &input_w, - RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); - - soc_camera_limit_side(&dummy, &input_h, - RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT); + v4l_bound_align_image(&input_w, 8, RJ54N1_MAX_WIDTH, 0, + &input_h, 8, RJ54N1_MAX_HEIGHT, 0, 0); output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; @@ -618,6 +616,9 @@ static int rj54n1_get_fmt(struct v4l2_subdev *sd, mf->code = rj54n1->fmt->code; mf->colorspace = rj54n1->fmt->colorspace; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->xfer_func = V4L2_XFER_FUNC_SRGB; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; mf->field = V4L2_FIELD_NONE; mf->width = rj54n1->width; mf->height = rj54n1->height; @@ -1163,10 +1164,27 @@ static int rj54n1_s_register(struct v4l2_subdev *sd, static int rj54n1_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct rj54n1 *rj54n1 = to_rj54n1(client); - return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on); + if (on) { + if (rj54n1->pwup_gpio) + gpiod_set_value(rj54n1->pwup_gpio, 1); + if (rj54n1->enable_gpio) + gpiod_set_value(rj54n1->enable_gpio, 1); + + msleep(1); + + return clk_prepare_enable(rj54n1->clk); + } + + clk_disable_unprepare(rj54n1->clk); + + if (rj54n1->enable_gpio) + gpiod_set_value(rj54n1->enable_gpio, 0); + if (rj54n1->pwup_gpio) + gpiod_set_value(rj54n1->pwup_gpio, 0); + + return 0; } static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1221,40 +1239,8 @@ static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { .s_power = rj54n1_s_power, }; -static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_RISING) - return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); - else - return reg_write(client, RJ54N1_OUT_SIGPO, 0); -} - static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_stream = rj54n1_s_stream, - .g_mbus_config = rj54n1_g_mbus_config, - .s_mbus_config = rj54n1_s_mbus_config, }; static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { @@ -1316,17 +1302,16 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct i2c_adapter *adapter = client->adapter; struct rj54n1_pdata *rj54n1_priv; int ret; - if (!ssdd || !ssdd->drv_priv) { + if (!client->dev.platform_data) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; } - rj54n1_priv = ssdd->drv_priv; + rj54n1_priv = client->dev.platform_data; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, @@ -1364,32 +1349,68 @@ static int rj54n1_probe(struct i2c_client *client, rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); - rj54n1->clk = v4l2_clk_get(&client->dev, "mclk"); + rj54n1->clk = clk_get(&client->dev, NULL); if (IS_ERR(rj54n1->clk)) { ret = PTR_ERR(rj54n1->clk); - goto eclkget; + goto err_free_ctrl; } - ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { - v4l2_clk_put(rj54n1->clk); -eclkget: - v4l2_ctrl_handler_free(&rj54n1->hdl); + rj54n1->pwup_gpio = gpiod_get_optional(&client->dev, "powerup", + GPIOD_OUT_LOW); + if (IS_ERR(rj54n1->pwup_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"powerup\": %ld\n", + PTR_ERR(rj54n1->pwup_gpio)); + ret = PTR_ERR(rj54n1->pwup_gpio); + goto err_clk_put; + } + + rj54n1->enable_gpio = gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(rj54n1->enable_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"enable\": %ld\n", + PTR_ERR(rj54n1->enable_gpio)); + ret = PTR_ERR(rj54n1->enable_gpio); + goto err_gpio_put; } + ret = rj54n1_video_probe(client, rj54n1_priv); + if (ret < 0) + goto err_gpio_put; + + ret = v4l2_async_register_subdev(&rj54n1->subdev); + if (ret) + goto err_gpio_put; + + return 0; + +err_gpio_put: + if (rj54n1->enable_gpio) + gpiod_put(rj54n1->enable_gpio); + + if (rj54n1->pwup_gpio) + gpiod_put(rj54n1->pwup_gpio); + +err_clk_put: + clk_put(rj54n1->clk); + +err_free_ctrl: + v4l2_ctrl_handler_free(&rj54n1->hdl); + return ret; } static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - v4l2_clk_put(rj54n1->clk); - v4l2_device_unregister_subdev(&rj54n1->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); + if (rj54n1->enable_gpio) + gpiod_put(rj54n1->enable_gpio); + if (rj54n1->pwup_gpio) + gpiod_put(rj54n1->pwup_gpio); + + clk_put(rj54n1->clk); v4l2_ctrl_handler_free(&rj54n1->hdl); + v4l2_async_unregister_subdev(&rj54n1->subdev); return 0; } -- cgit v1.2.3 From f3590dc329744dc8426dc9c76e37f4bd27ca8594 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 28 May 2018 12:37:09 -0400 Subject: media: arch: sh: kfr2r09: Use new renesas-ceu camera driver Use the new renesas-ceu camera driver in kfr2r09 board file instead of the soc_camera based sh_mobile_ceu_camera driver. Get rid of soc_camera specific components, and move clk and gpio handling away from board file, registering the clock source and the enable gpios for driver consumption. Memory for the CEU video buffers is now reserved with membocks APIs, and need to be declared as dma_coherent during machine initialization to remove that architecture specific part from CEU driver. While at there update license to SPDX header and sort headers alphabetically. No need to udapte the clock source names, as commit c2f9b05fd5c1 ("media: arch: sh: ecovec: Use new renesas-ceu camera driver") already updated it to the new ceu driver name for all SH7724 boards (possibly breaking kfr2r09 before this commit). Compile tested only. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-kfr2r09/setup.c | 217 +++++++++++++++++------------------- 1 file changed, 103 insertions(+), 114 deletions(-) diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 6af7777332fc..e59c577ed871 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -1,41 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 /* * KFR2R09 board support code * * Copyright (C) 2009 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ -#include -#include -#include -#include -#include -#include -#include + +#include +#include +#include +#include + +#include + +#include #include -#include +#include #include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include #include +#include #include #include +#include #include #include -#include + +#include + +#include #include -#include -#include + #include