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(+) (limited to 'drivers/media') 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-70-g09d2 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(+) (limited to 'drivers/media') 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-70-g09d2 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(+) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(+) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 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 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(-) (limited to 'drivers/media') 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-70-g09d2 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 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(+) (limited to 'drivers/media') 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-70-g09d2 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 (limited to 'drivers/media') 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-70-g09d2 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(-) (limited to 'drivers/media') 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-70-g09d2 From aa2f88712718d045855acc6686dbce6a7286d010 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 17 May 2018 10:30:15 -0400 Subject: media: v4l2-ioctl: create helper to fill in v4l2_standard for ENUMSTD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for adding a new IOCTL VIDIOC_SUBDEV_ENUMSTD which would enumerate the standards for a subdevice by breaking out the code which could be shared between the video and subdevice versions of this IOCTL. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: fixed 'sdandard' typos in v4l2-ioctl.h] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 66 ++++++++++++++++++++---------------- include/media/v4l2-ioctl.h | 15 ++++++-- 2 files changed, 50 insertions(+), 31 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index dd210067151f..eeed14468a17 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -125,6 +125,42 @@ int v4l2_video_std_construct(struct v4l2_standard *vs, } EXPORT_SYMBOL(v4l2_video_std_construct); +/* Fill in the fields of a v4l2_standard structure according to the + * 'id' and 'vs->index' parameters. Returns negative on error. */ +int v4l_video_std_enumstd(struct v4l2_standard *vs, v4l2_std_id id) +{ + v4l2_std_id curr_id = 0; + unsigned int index = vs->index, i, j = 0; + const char *descr = ""; + + /* Return -ENODATA if the id for the current input + or output is 0, meaning that it doesn't support this API. */ + if (id == 0) + return -ENODATA; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) + break; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; + } + if (i <= index) + return -EINVAL; + + v4l2_video_std_construct(vs, curr_id, descr); + return 0; +} + /* ----------------------------------------------------------------- */ /* some arrays for pretty-printing debug messages of enum types */ @@ -1753,36 +1789,8 @@ static int v4l_enumstd(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms, curr_id = 0; - unsigned int index = p->index, i, j = 0; - const char *descr = ""; - - /* Return -ENODATA if the tvnorms for the current input - or output is 0, meaning that it doesn't support this API. */ - if (id == 0) - return -ENODATA; - /* Return norm array in a canonical way */ - for (i = 0; i <= index && id; i++) { - /* last std value in the standards array is 0, so this - while always ends there since (id & 0) == 0. */ - while ((id & standards[j].std) != standards[j].std) - j++; - curr_id = standards[j].std; - descr = standards[j].descr; - j++; - if (curr_id == 0) - break; - if (curr_id != V4L2_STD_PAL && - curr_id != V4L2_STD_SECAM && - curr_id != V4L2_STD_NTSC) - id &= ~curr_id; - } - if (i <= index) - return -EINVAL; - - v4l2_video_std_construct(p, curr_id, descr); - return 0; + return v4l_video_std_enumstd(p, vfd->tvnorms); } static int v4l_s_std(const struct v4l2_ioctl_ops *ops, diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index a8dbf5b54b5c..5848d92c30da 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -621,7 +621,7 @@ const char *v4l2_norm_to_name(v4l2_std_id id); * v4l2_video_std_frame_period - Ancillary routine that fills a * struct &v4l2_fract pointer with the default framerate fraction. * - * @id: analog TV sdandard ID. + * @id: analog TV standard ID. * @frameperiod: struct &v4l2_fract pointer to be filled * */ @@ -632,7 +632,7 @@ void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod); * a &v4l2_standard structure according to the @id parameter. * * @vs: struct &v4l2_standard pointer to be filled - * @id: analog TV sdandard ID. + * @id: analog TV standard ID. * @name: name of the standard to be used * * .. note:: @@ -642,6 +642,17 @@ void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod); int v4l2_video_std_construct(struct v4l2_standard *vs, int id, const char *name); +/** + * v4l_video_std_enumstd - Ancillary routine that fills in the fields of + * a &v4l2_standard structure according to the @id and @vs->index + * parameters. + * + * @vs: struct &v4l2_standard pointer to be filled. + * @id: analog TV standard ID. + * + */ +int v4l_video_std_enumstd(struct v4l2_standard *vs, v4l2_std_id id); + /** * v4l_printk_ioctl - Ancillary routine that prints the ioctl in a * human-readable format. -- cgit v1.2.3-70-g09d2 From bfe75f76d7bee4513bc7851ba1699f4bc472d6ca Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:41:19 -0400 Subject: media: stm32-dcmi: increase max width/height to 2592 DCMI can capture 5Mp raw frames, increase limit accordingly. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 2e1933d872ee..db2dab335fa8 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -89,14 +89,9 @@ enum state { }; #define MIN_WIDTH 16U -#define MAX_WIDTH 2048U +#define MAX_WIDTH 2592U #define MIN_HEIGHT 16U -#define MAX_HEIGHT 2048U - -#define MIN_JPEG_WIDTH 16U -#define MAX_JPEG_WIDTH 2592U -#define MIN_JPEG_HEIGHT 16U -#define MAX_JPEG_HEIGHT 2592U +#define MAX_HEIGHT 2592U #define TIMEOUT_MS 1000 @@ -843,14 +838,8 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } /* Limit to hardware capabilities */ - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH); - pix->height = - clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT); - } else { - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); - } + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); /* No crop if JPEG is requested */ do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG); -- cgit v1.2.3-70-g09d2 From 4b84e75020a6b136e7160a9c53443b4c26e5d518 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:50:09 -0400 Subject: media: stm32-dcmi: code cleanup Minor non-functional fixes around comments, variable namings and trace point enhancement. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index db2dab335fa8..b78583dc972b 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -248,13 +248,12 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) static void dcmi_dma_callback(void *param) { struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; - struct dma_chan *chan = dcmi->dma_chan; struct dma_tx_state state; enum dma_status status; struct dcmi_buf *buf = dcmi->active; /* Check DMA status */ - status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); switch (status) { case DMA_IN_PROGRESS: @@ -308,10 +307,11 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, /* Prepare a DMA transaction */ desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, buf->size, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); if (!desc) { - dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n", - __func__, buf->size); + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n", + __func__, &buf->paddr, buf->size); return -EINVAL; } @@ -377,7 +377,6 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) { struct dma_tx_state state; enum dma_status status; - struct dma_chan *chan = dcmi->dma_chan; struct dcmi_buf *buf = dcmi->active; if (!buf) @@ -385,8 +384,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) /* * Because of variable JPEG buffer size sent by sensor, - * DMA transfer never completes due to transfer size - * never reached. + * DMA transfer never completes due to transfer size never reached. * In order to ensure that all the JPEG data are transferred * in active buffer memory, DMA is drained. * Then DMA tx status gives the amount of data transferred @@ -395,10 +393,10 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) */ /* Drain DMA */ - dmaengine_synchronize(chan); + dmaengine_synchronize(dcmi->dma_chan); /* Get DMA residue to get JPEG size */ - status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); if (status != DMA_ERROR && state.residue < buf->size) { /* Return JPEG buffer to V4L2 with received JPEG buffer size */ dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); -- cgit v1.2.3-70-g09d2 From b53a24fc2849a4d56961ac74a7aaeca9f5953a5a Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:50:24 -0400 Subject: media: stm32-dcmi: do not fall into error on buffer starvation Return silently instead of falling into error when running out of available buffers when restarting capture. Capture will be restarted when new buffers will be provided by V4L2 client. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b78583dc972b..fc72a57d8478 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -227,13 +227,10 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) /* Restart a new DMA transfer with next buffer */ if (list_empty(&dcmi->buffers)) { - dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", - __func__); - dcmi->errors_count++; + dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); dcmi->active = NULL; - spin_unlock_irq(&dcmi->irqlock); - return -EINVAL; + return 0; } dcmi->active = list_entry(dcmi->buffers.next, -- cgit v1.2.3-70-g09d2 From a331df3cb3a121ed9d6c2010f28ca0fccc4153ba Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:50:25 -0400 Subject: media: stm32-dcmi: return buffer in error state on dma error Return buffer to V4L2 in error state if DMA error occurs. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index fc72a57d8478..526d8311df4a 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -261,6 +261,9 @@ static void dcmi_dma_callback(void *param) break; case DMA_ERROR: dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__); + + /* Return buffer to V4L2 in error state */ + dcmi_buffer_done(dcmi, buf, 0, -EIO); break; case DMA_COMPLETE: dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); -- cgit v1.2.3-70-g09d2 From 2d494d4a09c027fd656862dc0a1aa7a14db42e2a Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:50:26 -0400 Subject: media: stm32-dcmi: clarify state logic on buffer starvation Introduce WAIT_FOR_BUFFER state instead of "active" field checking to manage buffer starvation case. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 526d8311df4a..61daa1e0514a 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -84,6 +84,7 @@ enum state { STOPPED = 0, + WAIT_FOR_BUFFER, RUNNING, STOPPING, }; @@ -229,6 +230,7 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) if (list_empty(&dcmi->buffers)) { dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); dcmi->active = NULL; + dcmi->state = WAIT_FOR_BUFFER; spin_unlock_irq(&dcmi->irqlock); return 0; } @@ -547,9 +549,11 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) spin_lock_irq(&dcmi->irqlock); - if (dcmi->state == RUNNING && !dcmi->active) { dcmi->active = buf; + if (dcmi->state == WAIT_FOR_BUFFER) { + dcmi->state = RUNNING; + dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); @@ -629,8 +633,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); - dcmi->state = RUNNING; - dcmi->sequence = 0; dcmi->errors_count = 0; dcmi->overrun_count = 0; @@ -643,6 +645,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) */ if (list_empty(&dcmi->buffers)) { dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer queueing\n"); + dcmi->state = WAIT_FOR_BUFFER; spin_unlock_irq(&dcmi->irqlock); return 0; } @@ -652,6 +655,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + dcmi->state = RUNNING; + spin_unlock_irq(&dcmi->irqlock); ret = dcmi_start_capture(dcmi); if (ret) { -- cgit v1.2.3-70-g09d2 From 49bcc1746ffbf94f41840718c5fab1a8d56c82d8 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:50:27 -0400 Subject: media: stm32-dcmi: revisit buffer list management Cleanup "active" field usage and enhance list management to avoid exceptions when releasing buffers on error or stopping streaming. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 65 +++++++++++++++---------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 61daa1e0514a..5d866ac69d5c 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -190,7 +190,7 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) reg_write(base, reg, reg_read(base, reg) & ~mask); } -static int dcmi_start_capture(struct stm32_dcmi *dcmi); +static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf); static void dcmi_buffer_done(struct stm32_dcmi *dcmi, struct dcmi_buf *buf, @@ -202,6 +202,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi, if (!buf) return; + list_del_init(&buf->list); + vbuf = &buf->vb; vbuf->sequence = dcmi->sequence++; @@ -219,6 +221,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi, static int dcmi_restart_capture(struct stm32_dcmi *dcmi) { + struct dcmi_buf *buf; + spin_lock_irq(&dcmi->irqlock); if (dcmi->state != RUNNING) { @@ -229,19 +233,16 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) /* Restart a new DMA transfer with next buffer */ if (list_empty(&dcmi->buffers)) { dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); - dcmi->active = NULL; dcmi->state = WAIT_FOR_BUFFER; spin_unlock_irq(&dcmi->irqlock); return 0; } - - dcmi->active = list_entry(dcmi->buffers.next, - struct dcmi_buf, list); - list_del_init(&dcmi->active->list); + buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); + dcmi->active = buf; spin_unlock_irq(&dcmi->irqlock); - return dcmi_start_capture(dcmi); + return dcmi_start_capture(dcmi, buf); } static void dcmi_dma_callback(void *param) @@ -251,6 +252,8 @@ static void dcmi_dma_callback(void *param) enum dma_status status; struct dcmi_buf *buf = dcmi->active; + spin_lock_irq(&dcmi->irqlock); + /* Check DMA status */ status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); @@ -273,15 +276,19 @@ static void dcmi_dma_callback(void *param) /* Return buffer to V4L2 */ dcmi_buffer_done(dcmi, buf, buf->size, 0); + spin_unlock_irq(&dcmi->irqlock); + /* Restart capture */ if (dcmi_restart_capture(dcmi)) dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", __func__); - break; + return; default: dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); break; } + + spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_dma(struct stm32_dcmi *dcmi, @@ -333,10 +340,9 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, return 0; } -static int dcmi_start_capture(struct stm32_dcmi *dcmi) +static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf) { int ret; - struct dcmi_buf *buf = dcmi->active; if (!buf) return -EINVAL; @@ -490,8 +496,6 @@ static int dcmi_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - dcmi->active = NULL; - dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n", *nbuffers, size); @@ -549,23 +553,24 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) spin_lock_irq(&dcmi->irqlock); - dcmi->active = buf; + /* Enqueue to video buffers list */ + list_add_tail(&buf->list, &dcmi->buffers); if (dcmi->state == WAIT_FOR_BUFFER) { dcmi->state = RUNNING; + dcmi->active = buf; dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); spin_unlock_irq(&dcmi->irqlock); - if (dcmi_start_capture(dcmi)) + if (dcmi_start_capture(dcmi, buf)) dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", __func__); - } else { - /* Enqueue to video buffers list */ - list_add_tail(&buf->list, &dcmi->buffers); - spin_unlock_irq(&dcmi->irqlock); + return; } + + spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -637,7 +642,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) dcmi->errors_count = 0; dcmi->overrun_count = 0; dcmi->buffers_count = 0; - dcmi->active = NULL; /* * Start transfer if at least one buffer has been queued, @@ -650,15 +654,15 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } - dcmi->active = list_entry(dcmi->buffers.next, struct dcmi_buf, list); - list_del_init(&dcmi->active->list); - - dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); + dcmi->active = buf; dcmi->state = RUNNING; + dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); + spin_unlock_irq(&dcmi->irqlock); - ret = dcmi_start_capture(dcmi); + ret = dcmi_start_capture(dcmi, buf); if (ret) { dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); @@ -682,15 +686,11 @@ err_release_buffers: * Return all buffers to vb2 in QUEUED state. * This will give ownership back to userspace */ - if (dcmi->active) { - buf = dcmi->active; - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - dcmi->active = NULL; - } list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { list_del_init(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } + dcmi->active = NULL; spin_unlock_irq(&dcmi->irqlock); return ret; @@ -732,16 +732,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) } /* Return all queued buffers to vb2 in ERROR state */ - if (dcmi->active) { - buf = dcmi->active; - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dcmi->active = NULL; - } list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { list_del_init(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } + dcmi->active = NULL; + spin_unlock_irq(&dcmi->irqlock); /* Stop all pending DMA operations */ -- cgit v1.2.3-70-g09d2 From fdaf59582c037ca5bd23f23ab28c1c5c384c0520 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 11 Jun 2018 05:52:17 -0400 Subject: media: stm32-dcmi: revisit stop streaming ops Do not wait for interrupt completion when stopping streaming, stopping sensor and disabling interruptions are enough. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 5d866ac69d5c..db49db78e00d 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -86,7 +86,6 @@ enum state { STOPPED = 0, WAIT_FOR_BUFFER, RUNNING, - STOPPING, }; #define MIN_WIDTH 16U @@ -431,18 +430,6 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) spin_lock_irq(&dcmi->irqlock); - /* Stop capture is required */ - if (dcmi->state == STOPPING) { - reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); - - dcmi->state = STOPPED; - - complete(&dcmi->complete); - - spin_unlock_irq(&dcmi->irqlock); - return IRQ_HANDLED; - } - if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) { dcmi->errors_count++; if (dcmi->misr & IT_OVR) @@ -700,8 +687,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); struct dcmi_buf *buf, *node; - unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS); - long timeout; int ret; /* Disable stream on the sub device */ @@ -710,13 +695,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n", __func__, ret); - spin_lock_irq(&dcmi->irqlock); - dcmi->state = STOPPING; - spin_unlock_irq(&dcmi->irqlock); - - timeout = wait_for_completion_interruptible_timeout(&dcmi->complete, - time_ms); - spin_lock_irq(&dcmi->irqlock); /* Disable interruptions */ @@ -725,12 +703,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* Disable DCMI */ reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); - if (!timeout) { - dev_err(dcmi->dev, "%s: Timeout during stop streaming\n", - __func__); - dcmi->state = STOPPED; - } - /* Return all queued buffers to vb2 in ERROR state */ list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { list_del_init(&buf->list); @@ -738,6 +710,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) } dcmi->active = NULL; + dcmi->state = STOPPED; spin_unlock_irq(&dcmi->irqlock); -- cgit v1.2.3-70-g09d2 From f8eb8e5c1f31f059677a80e0a902e64065695fa8 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 12 Jun 2018 13:22:17 -0400 Subject: media: stm32-dcmi: drop unnecessary while(1) loop The while(1) is effectively useless as all possible paths within it return thus there is no way to loop. Signed-off-by: Nicholas Mc Guire Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index db49db78e00d..9f6bb4165e54 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1567,23 +1567,21 @@ static int dcmi_graph_parse(struct stm32_dcmi *dcmi, 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 */ - dcmi->entity.node = remote; - dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - dcmi->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 */ + dcmi->entity.node = remote; + dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); + return 0; } static int dcmi_graph_init(struct stm32_dcmi *dcmi) -- cgit v1.2.3-70-g09d2 From c2ee2243ce3763fef8da097851b2c07b73e18cdb Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 12 Jun 2018 13:22:18 -0400 Subject: media: stm32-dcmi: add mandatory of_node_put() in success path The endpoint allocated by of_graph_get_next_endpoint() needs an of_node_put() in both error and success path. As ep is not used the refcount decrement can be right after the last use of ep. Fixes: commit 37404f91ef8b ("[media] stm32-dcmi: STM32 DCMI camera interface driver") Signed-off-by: Nicholas Mc Guire Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 9f6bb4165e54..fcad6de06009 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1572,10 +1572,9 @@ static int dcmi_graph_parse(struct stm32_dcmi *dcmi, 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 */ dcmi->entity.node = remote; -- cgit v1.2.3-70-g09d2 From f11552d030e8e5a0a945b1920e31eaf48fe3fad4 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 12 Jun 2018 13:23:16 -0400 Subject: media: stm32-dcmi: simplify of_node_put usage This does not fix any bug - this is just a code simplification. As np is not used after passing it to v4l2_fwnode_endpoint_parse() its refcount can be decremented immediately and at one location. Signed-off-by: Nicholas Mc Guire Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index fcad6de06009..4f65f14fbbc1 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1655,23 +1655,20 @@ static int dcmi_probe(struct platform_device *pdev) } ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); + of_node_put(np); if (ret) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); - of_node_put(np); return -ENODEV; } if (ep.bus_type == V4L2_MBUS_CSI2) { dev_err(&pdev->dev, "CSI bus not supported\n"); - of_node_put(np); return -ENODEV; } dcmi->bus.flags = ep.bus.parallel.flags; dcmi->bus.bus_width = ep.bus.parallel.bus_width; dcmi->bus.data_shift = ep.bus.parallel.data_shift; - of_node_put(np); - irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "Could not get irq\n"); -- cgit v1.2.3-70-g09d2 From 152e0bf60219ba589254c7aff4c095943c36cf68 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 13 Jun 2018 05:59:39 -0400 Subject: media: stm32-dcmi: add power saving support Implements runtime & system sleep power management ops. Signed-off-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 81 +++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 4f65f14fbbc1..721564176d8c 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include @@ -567,9 +569,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) u32 val = 0; int ret; - ret = clk_enable(dcmi->mclk); + ret = pm_runtime_get_sync(dcmi->dev); if (ret) { - dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n", + dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync\n", __func__); goto err_release_buffers; } @@ -579,7 +581,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret && ret != -ENOIOCTLCMD) { dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", __func__); - goto err_disable_clock; + goto err_pm_put; } spin_lock_irq(&dcmi->irqlock); @@ -664,8 +666,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) err_subdev_streamoff: v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); -err_disable_clock: - clk_disable(dcmi->mclk); +err_pm_put: + pm_runtime_put(dcmi->dev); err_release_buffers: spin_lock_irq(&dcmi->irqlock); @@ -717,7 +719,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* Stop all pending DMA operations */ dmaengine_terminate_all(dcmi->dma_chan); - clk_disable(dcmi->mclk); + pm_runtime_put(dcmi->dev); if (dcmi->errors_count) dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", @@ -1707,12 +1709,6 @@ static int dcmi_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - ret = clk_prepare(mclk); - if (ret) { - dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk); - goto err_dma_release; - } - spin_lock_init(&dcmi->irqlock); mutex_init(&dcmi->lock); init_completion(&dcmi->complete); @@ -1728,7 +1724,7 @@ static int dcmi_probe(struct platform_device *pdev) /* Initialize the top-level structure */ ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); if (ret) - goto err_clk_unprepare; + goto err_dma_release; dcmi->vdev = video_device_alloc(); if (!dcmi->vdev) { @@ -1788,14 +1784,15 @@ static int dcmi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probe done\n"); platform_set_drvdata(pdev, dcmi); + + pm_runtime_enable(&pdev->dev); + return 0; err_device_release: video_device_release(dcmi->vdev); err_device_unregister: v4l2_device_unregister(&dcmi->v4l2_dev); -err_clk_unprepare: - clk_unprepare(dcmi->mclk); err_dma_release: dma_release_channel(dcmi->dma_chan); @@ -1806,20 +1803,72 @@ static int dcmi_remove(struct platform_device *pdev) { struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + v4l2_async_notifier_unregister(&dcmi->notifier); v4l2_device_unregister(&dcmi->v4l2_dev); - clk_unprepare(dcmi->mclk); + dma_release_channel(dcmi->dma_chan); return 0; } +static __maybe_unused int dcmi_runtime_suspend(struct device *dev) +{ + struct stm32_dcmi *dcmi = dev_get_drvdata(dev); + + clk_disable_unprepare(dcmi->mclk); + + return 0; +} + +static __maybe_unused int dcmi_runtime_resume(struct device *dev) +{ + struct stm32_dcmi *dcmi = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dcmi->mclk); + if (ret) + dev_err(dev, "%s: Failed to prepare_enable clock\n", __func__); + + return ret; +} + +static __maybe_unused int dcmi_suspend(struct device *dev) +{ + /* disable clock */ + pm_runtime_force_suspend(dev); + + /* change pinctrl state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static __maybe_unused int dcmi_resume(struct device *dev) +{ + /* restore pinctl default state */ + pinctrl_pm_select_default_state(dev); + + /* clock enable */ + pm_runtime_force_resume(dev); + + return 0; +} + +static const struct dev_pm_ops dcmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) + SET_RUNTIME_PM_OPS(dcmi_runtime_suspend, + dcmi_runtime_resume, NULL) +}; + static struct platform_driver stm32_dcmi_driver = { .probe = dcmi_probe, .remove = dcmi_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), + .pm = &dcmi_pm_ops, }, }; -- cgit v1.2.3-70-g09d2 From cc00f5845af5ef2e0b8fb921517d57bf5d987917 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 7 Jun 2018 16:02:21 -0400 Subject: media: cx25821: remove cx25821-audio-upstream.c and cx25821-video-upstream.c Those two files are unused since commit 486a7a2813c7 ("[media] cx25821: remove cx25821-audio-upstream.c") and commit b6f21dc3541a ("[media] cx25821: remove video output support") Remove them from tree. Signed-off-by: Corentin Labbe Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx25821/cx25821-audio-upstream.c | 679 --------------------- drivers/media/pci/cx25821/cx25821-audio-upstream.h | 58 -- drivers/media/pci/cx25821/cx25821-video-upstream.c | 673 -------------------- drivers/media/pci/cx25821/cx25821-video-upstream.h | 135 ---- drivers/media/pci/cx25821/cx25821.h | 12 - 5 files changed, 1557 deletions(-) delete mode 100644 drivers/media/pci/cx25821/cx25821-audio-upstream.c delete mode 100644 drivers/media/pci/cx25821/cx25821-audio-upstream.h delete mode 100644 drivers/media/pci/cx25821/cx25821-video-upstream.c delete mode 100644 drivers/media/pci/cx25821/cx25821-video-upstream.h (limited to 'drivers/media') diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c deleted file mode 100644 index ada26d4acfb4..000000000000 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors , - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "cx25821-video.h" -#include "cx25821-audio-upstream.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Hiep Huynh "); -MODULE_LICENSE("GPL"); - -static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | - FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; - -static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc) -{ - unsigned int i, lines; - u32 cdt; - - if (ch->cmds_start == 0) { - cx_write(ch->ptr1_reg, 0); - cx_write(ch->ptr2_reg, 0); - cx_write(ch->cnt2_reg, 0); - cx_write(ch->cnt1_reg, 0); - return 0; - } - - bpl = (bpl + 7) & ~7; /* alignment */ - cdt = ch->cdt; - lines = ch->fifo_size / bpl; - - if (lines > 3) - lines = 3; - - BUG_ON(lines < 2); - - /* write CDT */ - for (i = 0; i < lines; i++) { - cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); - cx_write(cdt + 16 * i + 4, 0); - cx_write(cdt + 16 * i + 8, 0); - cx_write(cdt + 16 * i + 12, 0); - } - - /* write CMDS */ - cx_write(ch->cmds_start + 0, risc); - - cx_write(ch->cmds_start + 4, 0); - cx_write(ch->cmds_start + 8, cdt); - cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); - cx_write(ch->cmds_start + 16, ch->ctrl_start); - - /* IQ size */ - cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); - - for (i = 24; i < 80; i += 4) - cx_write(ch->cmds_start + i, 0); - - /* fill registers */ - cx_write(ch->ptr1_reg, ch->fifo_start); - cx_write(ch->ptr2_reg, cdt); - cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); - cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); - - return 0; -} - -static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, - __le32 *rp, - dma_addr_t databuf_phys_addr, - unsigned int bpl, - int fifo_enable) -{ - unsigned int line; - const struct sram_channel *sram_ch = - dev->channels[dev->_audio_upstream_channel].sram_channels; - int offset = 0; - - /* scan lines */ - for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(databuf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - /* Check if we need to enable the FIFO - * after the first 3 lines. - * For the upstream audio channel, - * the risc engine will enable the FIFO */ - if (fifo_enable && line == 2) { - *(rp++) = RISC_WRITECR; - *(rp++) = sram_ch->dma_ctl; - *(rp++) = sram_ch->fld_aud_fifo_en; - *(rp++) = 0x00000020; - } - - offset += AUDIO_LINE_SIZE; - } - - return rp; -} - -static int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, - struct pci_dev *pci, - unsigned int bpl, unsigned int lines) -{ - __le32 *rp; - int fifo_enable = 0; - int frame = 0, i = 0; - int frame_size = AUDIO_DATA_BUF_SZ; - int databuf_offset = 0; - int risc_flag = RISC_CNT_INC; - dma_addr_t risc_phys_jump_addr; - - /* Virtual address of Risc buffer program */ - rp = dev->_risc_virt_addr; - - /* sync instruction */ - *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); - - for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { - databuf_offset = frame_size * frame; - - if (frame == 0) { - fifo_enable = 1; - risc_flag = RISC_CNT_RESET; - } else { - fifo_enable = 0; - risc_flag = RISC_CNT_INC; - } - - /* Calculate physical jump address */ - if ((frame + 1) == NUM_AUDIO_FRAMES) { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE; - } else { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE + - AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); - } - - rp = cx25821_risc_field_upstream_audio(dev, rp, - dev->_audiodata_buf_phys_addr + databuf_offset, - bpl, fifo_enable); - - if (USE_RISC_NOOP_AUDIO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* Loop to (Nth)FrameRISC or to Start of Risc program & - * generate IRQ */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - - /* Recalculate virtual address based on frame index */ - rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + - (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); - } - - return 0; -} - -static void cx25821_free_memory_audio(struct cx25821_dev *dev) -{ - if (dev->_risc_virt_addr) { - pci_free_consistent(dev->pci, dev->_audiorisc_size, - dev->_risc_virt_addr, dev->_risc_phys_addr); - dev->_risc_virt_addr = NULL; - } - - if (dev->_audiodata_buf_virt_addr) { - pci_free_consistent(dev->pci, dev->_audiodata_buf_size, - dev->_audiodata_buf_virt_addr, - dev->_audiodata_buf_phys_addr); - dev->_audiodata_buf_virt_addr = NULL; - } -} - -void cx25821_stop_upstream_audio(struct cx25821_dev *dev) -{ - const struct sram_channel *sram_ch = - dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; - u32 tmp = 0; - - if (!dev->_audio_is_running) { - printk(KERN_DEBUG - pr_fmt("No audio file is currently running so return!\n")); - return; - } - /* Disable RISC interrupts */ - cx_write(sram_ch->int_msk, 0); - - /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ - tmp = cx_read(sram_ch->dma_ctl); - cx_write(sram_ch->dma_ctl, - tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); - - /* Clear data buffer memory */ - if (dev->_audiodata_buf_virt_addr) - memset(dev->_audiodata_buf_virt_addr, 0, - dev->_audiodata_buf_size); - - dev->_audio_is_running = 0; - dev->_is_first_audio_frame = 0; - dev->_audioframe_count = 0; - dev->_audiofile_status = END_OF_FILE; - - flush_work(&dev->_audio_work_entry); - - kfree(dev->_audiofilename); -} - -void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) -{ - if (dev->_audio_is_running) - cx25821_stop_upstream_audio(dev); - - cx25821_free_memory_audio(dev); -} - -static int cx25821_get_audio_data(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - struct file *file; - int frame_index_temp = dev->_audioframe_index; - int i = 0; - int frame_size = AUDIO_DATA_BUF_SZ; - int frame_offset = frame_size * frame_index_temp; - char mybuf[AUDIO_LINE_SIZE]; - loff_t file_offset = dev->_audioframe_count * frame_size; - char *p = NULL; - - if (dev->_audiofile_status == END_OF_FILE) - return 0; - - file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(file)) { - pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", - __func__, dev->_audiofilename, -PTR_ERR(file)); - return PTR_ERR(file); - } - - if (dev->_audiodata_buf_virt_addr) - p = (char *)dev->_audiodata_buf_virt_addr + frame_offset; - - for (i = 0; i < dev->_audio_lines_count; i++) { - int n = kernel_read(file, mybuf, AUDIO_LINE_SIZE, &file_offset); - if (n < AUDIO_LINE_SIZE) { - pr_info("Done: exit %s() since no more bytes to read from Audio file\n", - __func__); - dev->_audiofile_status = END_OF_FILE; - fput(file); - return 0; - } - dev->_audiofile_status = IN_PROGRESS; - if (p) { - memcpy(p, mybuf, n); - p += n; - } - } - dev->_audioframe_count++; - fput(file); - - return 0; -} - -static void cx25821_audioups_handler(struct work_struct *work) -{ - struct cx25821_dev *dev = container_of(work, struct cx25821_dev, - _audio_work_entry); - - if (!dev) { - pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", - __func__); - return; - } - - cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel]. - sram_channels); -} - -static int cx25821_openfile_audio(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - char *p = (void *)dev->_audiodata_buf_virt_addr; - struct file *file; - loff_t file_offset = 0; - int i, j; - - file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(file)) { - pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", - __func__, dev->_audiofilename, PTR_ERR(file)); - return PTR_ERR(file); - } - - for (j = 0; j < NUM_AUDIO_FRAMES; j++) { - for (i = 0; i < dev->_audio_lines_count; i++) { - char buf[AUDIO_LINE_SIZE]; - loff_t offset = file_offset; - int n = kernel_read(file, buf, AUDIO_LINE_SIZE, &file_offset); - - if (n < AUDIO_LINE_SIZE) { - pr_info("Done: exit %s() since no more bytes to read from Audio file\n", - __func__); - dev->_audiofile_status = END_OF_FILE; - fput(file); - return 0; - } - - if (p) - memcpy(p + offset, buf, n); - } - dev->_audioframe_count++; - } - dev->_audiofile_status = IN_PROGRESS; - fput(file); - return 0; -} - -static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, - const struct sram_channel *sram_ch, - int bpl) -{ - int ret = 0; - dma_addr_t dma_addr; - dma_addr_t data_dma_addr; - - cx25821_free_memory_audio(dev); - - dev->_risc_virt_addr = pci_alloc_consistent(dev->pci, - dev->audio_upstream_riscbuf_size, &dma_addr); - dev->_risc_virt_start_addr = dev->_risc_virt_addr; - dev->_risc_phys_start_addr = dma_addr; - dev->_risc_phys_addr = dma_addr; - dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; - - if (!dev->_risc_virt_addr) { - printk(KERN_DEBUG - pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n")); - return -ENOMEM; - } - /* Clear out memory at address */ - memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); - - /* For Audio Data buffer allocation */ - dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, - dev->audio_upstream_databuf_size, &data_dma_addr); - dev->_audiodata_buf_phys_addr = data_dma_addr; - dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; - - if (!dev->_audiodata_buf_virt_addr) { - printk(KERN_DEBUG - pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n")); - return -ENOMEM; - } - /* Clear out memory at address */ - memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); - - ret = cx25821_openfile_audio(dev, sram_ch); - if (ret < 0) - return ret; - - /* Creating RISC programs */ - ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, - dev->_audio_lines_count); - if (ret < 0) { - printk(KERN_DEBUG - pr_fmt("ERROR creating audio upstream RISC programs!\n")); - goto error; - } - - return 0; - -error: - return ret; -} - -static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, - u32 status) -{ - int i = 0; - u32 int_msk_tmp; - const struct sram_channel *channel = dev->channels[chan_num].sram_channels; - dma_addr_t risc_phys_jump_addr; - __le32 *rp; - - if (status & FLD_AUD_SRC_RISCI1) { - /* Get interrupt_index of the program that interrupted */ - u32 prog_cnt = cx_read(channel->gpcnt); - - /* Since we've identified our IRQ, clear our bits from the - * interrupt mask and interrupt status registers */ - cx_write(channel->int_msk, 0); - cx_write(channel->int_stat, cx_read(channel->int_stat)); - - spin_lock(&dev->slock); - - while (prog_cnt != dev->_last_index_irq) { - /* Update _last_index_irq */ - if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) - dev->_last_index_irq++; - else - dev->_last_index_irq = 0; - - dev->_audioframe_index = dev->_last_index_irq; - - schedule_work(&dev->_audio_work_entry); - } - - if (dev->_is_first_audio_frame) { - dev->_is_first_audio_frame = 0; - - if (dev->_risc_virt_start_addr != NULL) { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE + - AUDIO_RISC_DMA_BUF_SIZE; - - rp = cx25821_risc_field_upstream_audio(dev, - dev->_risc_virt_start_addr + 1, - dev->_audiodata_buf_phys_addr, - AUDIO_LINE_SIZE, FIFO_DISABLE); - - if (USE_RISC_NOOP_AUDIO) { - for (i = 0; i < NUM_NO_OPS; i++) { - *(rp++) = - cpu_to_le32(RISC_NOOP); - } - } - /* Jump to 2nd Audio Frame */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | - RISC_CNT_RESET); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - } - - spin_unlock(&dev->slock); - } else { - if (status & FLD_AUD_SRC_OF) - pr_warn("%s(): Audio Received Overflow Error Interrupt!\n", - __func__); - - if (status & FLD_AUD_SRC_SYNC) - pr_warn("%s(): Audio Received Sync Error Interrupt!\n", - __func__); - - if (status & FLD_AUD_SRC_OPC_ERR) - pr_warn("%s(): Audio Received OpCode Error Interrupt!\n", - __func__); - - /* Read and write back the interrupt status register to clear - * our bits */ - cx_write(channel->int_stat, cx_read(channel->int_stat)); - } - - if (dev->_audiofile_status == END_OF_FILE) { - pr_warn("EOF Channel Audio Framecount = %d\n", - dev->_audioframe_count); - return -1; - } - /* ElSE, set the interrupt mask register, re-enable irq. */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); - - return 0; -} - -static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) -{ - struct cx25821_dev *dev = dev_id; - u32 audio_status; - int handled = 0; - const struct sram_channel *sram_ch; - - if (!dev) - return -1; - - sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; - - audio_status = cx_read(sram_ch->int_stat); - - /* Only deal with our interrupt */ - if (audio_status) { - handled = cx25821_audio_upstream_irq(dev, - dev->_audio_upstream_channel, audio_status); - } - - if (handled < 0) - cx25821_stop_upstream_audio(dev); - else - handled += handled; - - return IRQ_RETVAL(handled); -} - -static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - int count = 0; - u32 tmp; - - do { - /* Wait 10 microsecond before checking to see if the FIFO is - * turned ON. */ - udelay(10); - - tmp = cx_read(sram_ch->dma_ctl); - - /* 10 millisecond timeout */ - if (count++ > 1000) { - pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n", - __func__); - return; - } - - } while (!(tmp & sram_ch->fld_aud_fifo_en)); - -} - -static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - u32 tmp = 0; - int err = 0; - - /* Set the physical start address of the RISC program in the initial - * program counter(IPC) member of the CMDS. */ - cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); - /* Risc IPC High 64 bits 63-32 */ - cx_write(sram_ch->cmds_start + 4, 0); - - /* reset counter */ - cx_write(sram_ch->gpcnt_ctl, 3); - - /* Set the line length (It looks like we do not need to set the - * line length) */ - cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); - - /* Set the input mode to 16-bit */ - tmp = cx_read(sram_ch->aud_cfg); - tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | - FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | - FLD_AUD_SONY_MODE; - cx_write(sram_ch->aud_cfg, tmp); - - /* Read and write back the interrupt status register to clear it */ - tmp = cx_read(sram_ch->int_stat); - cx_write(sram_ch->int_stat, tmp); - - /* Clear our bits from the interrupt status register. */ - cx_write(sram_ch->int_stat, _intr_msk); - - /* Set the interrupt mask register, enable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp |= _intr_msk); - - err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - pr_err("%s: can't get upstream IRQ %d\n", dev->name, - dev->pci->irq); - goto fail_irq; - } - - /* Start the DMA engine */ - tmp = cx_read(sram_ch->dma_ctl); - cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); - - dev->_audio_is_running = 1; - dev->_is_first_audio_frame = 1; - - /* The fifo_en bit turns on by the first Risc program */ - cx25821_wait_fifo_enable(dev, sram_ch); - - return 0; - -fail_irq: - cx25821_dev_unregister(dev); - return err; -} - -int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) -{ - const struct sram_channel *sram_ch; - int err = 0; - - if (dev->_audio_is_running) { - pr_warn("Audio Channel is still running so return!\n"); - return 0; - } - - dev->_audio_upstream_channel = channel_select; - sram_ch = dev->channels[channel_select].sram_channels; - - /* Work queue */ - INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); - - dev->_last_index_irq = 0; - dev->_audio_is_running = 0; - dev->_audioframe_count = 0; - dev->_audiofile_status = RESET_STATUS; - dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; - _line_size = AUDIO_LINE_SIZE; - - if ((dev->input_audiofilename) && - (strcmp(dev->input_audiofilename, "") != 0)) - dev->_audiofilename = kstrdup(dev->input_audiofilename, - GFP_KERNEL); - else - dev->_audiofilename = kstrdup(_defaultAudioName, - GFP_KERNEL); - - if (!dev->_audiofilename) { - err = -ENOMEM; - goto error; - } - - cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, - _line_size, 0); - - dev->audio_upstream_riscbuf_size = - AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + - RISC_SYNC_INSTRUCTION_SIZE; - dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; - - /* Allocating buffers and prepare RISC program */ - err = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, - _line_size); - if (err < 0) { - pr_err("%s: Failed to set up Audio upstream buffers!\n", - dev->name); - goto error; - } - /* Start RISC engine */ - cx25821_start_audio_dma_upstream(dev, sram_ch); - - return 0; - -error: - cx25821_dev_unregister(dev); - - return err; -} diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.h b/drivers/media/pci/cx25821/cx25821-audio-upstream.h deleted file mode 100644 index 2bc875d1ec9f..000000000000 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors , - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#include -#include - -#define NUM_AUDIO_PROGS 8 -#define NUM_AUDIO_FRAMES 8 -#define END_OF_FILE 0 -#define IN_PROGRESS 1 -#define RESET_STATUS -1 -#define FIFO_DISABLE 0 -#define FIFO_ENABLE 1 -#define NUM_NO_OPS 4 - -#define RISC_READ_INSTRUCTION_SIZE 12 -#define RISC_JUMP_INSTRUCTION_SIZE 12 -#define RISC_WRITECR_INSTRUCTION_SIZE 16 -#define RISC_SYNC_INSTRUCTION_SIZE 4 -#define DWORD_SIZE 4 -#define AUDIO_SYNC_LINE 4 - -#define LINES_PER_AUDIO_BUFFER 15 -#define AUDIO_LINE_SIZE 128 -#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER) - -#define USE_RISC_NOOP_AUDIO 1 - -#ifdef USE_RISC_NOOP_AUDIO -#define AUDIO_RISC_DMA_BUF_SIZE \ - (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \ - RISC_JUMP_INSTRUCTION_SIZE) -#endif - -#ifndef USE_RISC_NOOP_AUDIO -#define AUDIO_RISC_DMA_BUF_SIZE \ - (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE) -#endif - -static int _line_size; -char *_defaultAudioName = "/root/audioGOOD.wav"; diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c deleted file mode 100644 index 6c838c8a7924..000000000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.c +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors , - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "cx25821-video.h" -#include "cx25821-video-upstream.h" - -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Hiep Huynh "); -MODULE_LICENSE("GPL"); - -static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | - FLD_VID_SRC_OPC_ERR; - -int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc) -{ - unsigned int i, lines; - u32 cdt; - - if (ch->cmds_start == 0) { - cx_write(ch->ptr1_reg, 0); - cx_write(ch->ptr2_reg, 0); - cx_write(ch->cnt2_reg, 0); - cx_write(ch->cnt1_reg, 0); - return 0; - } - - bpl = (bpl + 7) & ~7; /* alignment */ - cdt = ch->cdt; - lines = ch->fifo_size / bpl; - - if (lines > 4) - lines = 4; - - BUG_ON(lines < 2); - - /* write CDT */ - for (i = 0; i < lines; i++) { - cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); - cx_write(cdt + 16 * i + 4, 0); - cx_write(cdt + 16 * i + 8, 0); - cx_write(cdt + 16 * i + 12, 0); - } - - /* write CMDS */ - cx_write(ch->cmds_start + 0, risc); - - cx_write(ch->cmds_start + 4, 0); - cx_write(ch->cmds_start + 8, cdt); - cx_write(ch->cmds_start + 12, (lines * 16) >> 3); - cx_write(ch->cmds_start + 16, ch->ctrl_start); - - cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW); - - for (i = 24; i < 80; i += 4) - cx_write(ch->cmds_start + i, 0); - - /* fill registers */ - cx_write(ch->ptr1_reg, ch->fifo_start); - cx_write(ch->ptr2_reg, cdt); - cx_write(ch->cnt2_reg, (lines * 16) >> 3); - cx_write(ch->cnt1_reg, (bpl >> 3) - 1); - - return 0; -} - -static __le32 *cx25821_update_riscprogram(struct cx25821_channel *chan, - __le32 *rp, unsigned int offset, - unsigned int bpl, u32 sync_line, - unsigned int lines, int fifo_enable, - int field_type) -{ - struct cx25821_video_out_data *out = chan->out; - unsigned int line, i; - int dist_betwn_starts = bpl * 2; - - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(out->_data_buf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) { - offset += dist_betwn_starts; - } - } - - return rp; -} - -static __le32 *cx25821_risc_field_upstream(struct cx25821_channel *chan, __le32 *rp, - dma_addr_t databuf_phys_addr, - unsigned int offset, u32 sync_line, - unsigned int bpl, unsigned int lines, - int fifo_enable, int field_type) -{ - struct cx25821_video_out_data *out = chan->out; - unsigned int line, i; - const struct sram_channel *sram_ch = chan->sram_channels; - int dist_betwn_starts = bpl * 2; - - /* sync instruction */ - if (sync_line != NO_SYNC_LINE) - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(databuf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) - /* to skip the other field line */ - offset += dist_betwn_starts; - - /* check if we need to enable the FIFO after the first 4 lines - * For the upstream video channel, the risc engine will enable - * the FIFO. */ - if (fifo_enable && line == 3) { - *(rp++) = cpu_to_le32(RISC_WRITECR); - *(rp++) = cpu_to_le32(sram_ch->dma_ctl); - *(rp++) = cpu_to_le32(FLD_VID_FIFO_EN); - *(rp++) = cpu_to_le32(0x00000001); - } - } - - return rp; -} - -static int cx25821_risc_buffer_upstream(struct cx25821_channel *chan, - struct pci_dev *pci, - unsigned int top_offset, - unsigned int bpl, unsigned int lines) -{ - struct cx25821_video_out_data *out = chan->out; - __le32 *rp; - int fifo_enable = 0; - /* get line count for single field */ - int singlefield_lines = lines >> 1; - int odd_num_lines = singlefield_lines; - int frame = 0; - int frame_size = 0; - int databuf_offset = 0; - int risc_program_size = 0; - int risc_flag = RISC_CNT_RESET; - unsigned int bottom_offset = bpl; - dma_addr_t risc_phys_jump_addr; - - if (out->is_60hz) { - odd_num_lines = singlefield_lines + 1; - risc_program_size = FRAME1_VID_PROG_SIZE; - frame_size = (bpl == Y411_LINE_SZ) ? - FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; - } else { - risc_program_size = PAL_VID_PROG_SIZE; - frame_size = (bpl == Y411_LINE_SZ) ? - FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - } - - /* Virtual address of Risc buffer program */ - rp = out->_dma_virt_addr; - - for (frame = 0; frame < NUM_FRAMES; frame++) { - databuf_offset = frame_size * frame; - - if (UNSET != top_offset) { - fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; - rp = cx25821_risc_field_upstream(chan, rp, - out->_data_buf_phys_addr + - databuf_offset, top_offset, 0, bpl, - odd_num_lines, fifo_enable, ODD_FIELD); - } - - fifo_enable = FIFO_DISABLE; - - /* Even Field */ - rp = cx25821_risc_field_upstream(chan, rp, - out->_data_buf_phys_addr + - databuf_offset, bottom_offset, - 0x200, bpl, singlefield_lines, - fifo_enable, EVEN_FIELD); - - if (frame == 0) { - risc_flag = RISC_CNT_RESET; - risc_phys_jump_addr = out->_dma_phys_start_addr + - risc_program_size; - } else { - risc_phys_jump_addr = out->_dma_phys_start_addr; - risc_flag = RISC_CNT_INC; - } - - /* Loop to 2ndFrameRISC or to Start of Risc - * program & generate IRQ - */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - - return 0; -} - -void cx25821_stop_upstream_video(struct cx25821_channel *chan) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch = chan->sram_channels; - u32 tmp = 0; - - if (!out->_is_running) { - pr_info("No video file is currently running so return!\n"); - return; - } - - /* Set the interrupt mask register, disable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) & ~(1 << sram_ch->irq_bit)); - - /* Disable RISC interrupts */ - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp & ~_intr_msk); - - /* Turn OFF risc and fifo enable */ - tmp = cx_read(sram_ch->dma_ctl); - cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - - free_irq(dev->pci->irq, chan); - - /* Clear data buffer memory */ - if (out->_data_buf_virt_addr) - memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); - - out->_is_running = 0; - out->_is_first_frame = 0; - out->_frame_count = 0; - out->_file_status = END_OF_FILE; - - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); -} - -void cx25821_free_mem_upstream(struct cx25821_channel *chan) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - - if (out->_is_running) - cx25821_stop_upstream_video(chan); - - if (out->_dma_virt_addr) { - pci_free_consistent(dev->pci, out->_risc_size, - out->_dma_virt_addr, out->_dma_phys_addr); - out->_dma_virt_addr = NULL; - } - - if (out->_data_buf_virt_addr) { - pci_free_consistent(dev->pci, out->_data_buf_size, - out->_data_buf_virt_addr, - out->_data_buf_phys_addr); - out->_data_buf_virt_addr = NULL; - } -} - -int cx25821_write_frame(struct cx25821_channel *chan, - const char __user *data, size_t count) -{ - struct cx25821_video_out_data *out = chan->out; - int line_size = (out->_pixel_format == PIXEL_FRMT_411) ? - Y411_LINE_SZ : Y422_LINE_SZ; - int frame_size = 0; - int frame_offset = 0; - int curpos = out->curpos; - - if (out->is_60hz) - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; - else - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - - if (curpos == 0) { - out->cur_frame_index = out->_frame_index; - if (wait_event_interruptible(out->waitq, out->cur_frame_index != out->_frame_index)) - return -EINTR; - out->cur_frame_index = out->_frame_index; - } - - frame_offset = out->cur_frame_index ? frame_size : 0; - - if (frame_size - curpos < count) - count = frame_size - curpos; - if (copy_from_user((__force char *)out->_data_buf_virt_addr + frame_offset + curpos, - data, count)) - return -EFAULT; - curpos += count; - if (curpos == frame_size) { - out->_frame_count++; - curpos = 0; - } - out->curpos = curpos; - - return count; -} - -static int cx25821_upstream_buffer_prepare(struct cx25821_channel *chan, - const struct sram_channel *sram_ch, - int bpl) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - int ret = 0; - dma_addr_t dma_addr; - dma_addr_t data_dma_addr; - - if (out->_dma_virt_addr != NULL) - pci_free_consistent(dev->pci, out->upstream_riscbuf_size, - out->_dma_virt_addr, out->_dma_phys_addr); - - out->_dma_virt_addr = pci_alloc_consistent(dev->pci, - out->upstream_riscbuf_size, &dma_addr); - out->_dma_virt_start_addr = out->_dma_virt_addr; - out->_dma_phys_start_addr = dma_addr; - out->_dma_phys_addr = dma_addr; - out->_risc_size = out->upstream_riscbuf_size; - - if (!out->_dma_virt_addr) { - pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); - return -ENOMEM; - } - - /* Clear memory at address */ - memset(out->_dma_virt_addr, 0, out->_risc_size); - - if (out->_data_buf_virt_addr != NULL) - pci_free_consistent(dev->pci, out->upstream_databuf_size, - out->_data_buf_virt_addr, - out->_data_buf_phys_addr); - /* For Video Data buffer allocation */ - out->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, - out->upstream_databuf_size, &data_dma_addr); - out->_data_buf_phys_addr = data_dma_addr; - out->_data_buf_size = out->upstream_databuf_size; - - if (!out->_data_buf_virt_addr) { - pr_err("FAILED to allocate memory for data buffer! Returning\n"); - return -ENOMEM; - } - - /* Clear memory at address */ - memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); - - /* Create RISC programs */ - ret = cx25821_risc_buffer_upstream(chan, dev->pci, 0, bpl, - out->_lines_count); - if (ret < 0) { - pr_info("Failed creating Video Upstream Risc programs!\n"); - goto error; - } - - return 0; - -error: - return ret; -} - -static int cx25821_video_upstream_irq(struct cx25821_channel *chan, u32 status) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - u32 int_msk_tmp; - const struct sram_channel *channel = chan->sram_channels; - int singlefield_lines = NTSC_FIELD_HEIGHT; - int line_size_in_bytes = Y422_LINE_SZ; - int odd_risc_prog_size = 0; - dma_addr_t risc_phys_jump_addr; - __le32 *rp; - - if (status & FLD_VID_SRC_RISC1) { - /* We should only process one program per call */ - u32 prog_cnt = cx_read(channel->gpcnt); - - /* Since we've identified our IRQ, clear our bits from the - * interrupt mask and interrupt status registers */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); - cx_write(channel->int_stat, _intr_msk); - - wake_up(&out->waitq); - - spin_lock(&dev->slock); - - out->_frame_index = prog_cnt; - - if (out->_is_first_frame) { - out->_is_first_frame = 0; - - if (out->is_60hz) { - singlefield_lines += 1; - odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; - } else { - singlefield_lines = PAL_FIELD_HEIGHT; - odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; - } - - if (out->_dma_virt_start_addr != NULL) { - line_size_in_bytes = - (out->_pixel_format == - PIXEL_FRMT_411) ? Y411_LINE_SZ : - Y422_LINE_SZ; - risc_phys_jump_addr = - out->_dma_phys_start_addr + - odd_risc_prog_size; - - rp = cx25821_update_riscprogram(chan, - out->_dma_virt_start_addr, TOP_OFFSET, - line_size_in_bytes, 0x0, - singlefield_lines, FIFO_DISABLE, - ODD_FIELD); - - /* Jump to Even Risc program of 1st Frame */ - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - } - - spin_unlock(&dev->slock); - } else { - if (status & FLD_VID_SRC_UF) - pr_err("%s(): Video Received Underflow Error Interrupt!\n", - __func__); - - if (status & FLD_VID_SRC_SYNC) - pr_err("%s(): Video Received Sync Error Interrupt!\n", - __func__); - - if (status & FLD_VID_SRC_OPC_ERR) - pr_err("%s(): Video Received OpCode Error Interrupt!\n", - __func__); - } - - if (out->_file_status == END_OF_FILE) { - pr_err("EOF Channel 1 Framecount = %d\n", out->_frame_count); - return -1; - } - /* ElSE, set the interrupt mask register, re-enable irq. */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); - - return 0; -} - -static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) -{ - struct cx25821_channel *chan = dev_id; - struct cx25821_dev *dev = chan->dev; - u32 vid_status; - int handled = 0; - const struct sram_channel *sram_ch; - - if (!dev) - return -1; - - sram_ch = chan->sram_channels; - - vid_status = cx_read(sram_ch->int_stat); - - /* Only deal with our interrupt */ - if (vid_status) - handled = cx25821_video_upstream_irq(chan, vid_status); - - return IRQ_RETVAL(handled); -} - -static void cx25821_set_pixelengine(struct cx25821_channel *chan, - const struct sram_channel *ch, - int pix_format) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - int width = WIDTH_D1; - int height = out->_lines_count; - int num_lines, odd_num_lines; - u32 value; - int vip_mode = OUTPUT_FRMT_656; - - value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); - value &= 0xFFFFFFEF; - value |= out->is_60hz ? 0 : 0x10; - cx_write(ch->vid_fmt_ctl, value); - - /* set number of active pixels in each line. - * Default is 720 pixels in both NTSC and PAL format */ - cx_write(ch->vid_active_ctl1, width); - - num_lines = (height / 2) & 0x3FF; - odd_num_lines = num_lines; - - if (out->is_60hz) - odd_num_lines += 1; - - value = (num_lines << 16) | odd_num_lines; - - /* set number of active lines in field 0 (top) and field 1 (bottom) */ - cx_write(ch->vid_active_ctl2, value); - - cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); -} - -static int cx25821_start_video_dma_upstream(struct cx25821_channel *chan, - const struct sram_channel *sram_ch) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - u32 tmp = 0; - int err = 0; - - /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for - * channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - /* Set the physical start address of the RISC program in the initial - * program counter(IPC) member of the cmds. - */ - cx_write(sram_ch->cmds_start + 0, out->_dma_phys_addr); - /* Risc IPC High 64 bits 63-32 */ - cx_write(sram_ch->cmds_start + 4, 0); - - /* reset counter */ - cx_write(sram_ch->gpcnt_ctl, 3); - - /* Clear our bits from the interrupt status register. */ - cx_write(sram_ch->int_stat, _intr_msk); - - /* Set the interrupt mask register, enable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp |= _intr_msk); - - err = request_irq(dev->pci->irq, cx25821_upstream_irq, - IRQF_SHARED, dev->name, chan); - if (err < 0) { - pr_err("%s: can't get upstream IRQ %d\n", - dev->name, dev->pci->irq); - goto fail_irq; - } - - /* Start the DMA engine */ - tmp = cx_read(sram_ch->dma_ctl); - cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); - - out->_is_running = 1; - out->_is_first_frame = 1; - - return 0; - -fail_irq: - cx25821_dev_unregister(dev); - return err; -} - -int cx25821_vidupstream_init(struct cx25821_channel *chan, - int pixel_format) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch; - u32 tmp; - int err = 0; - int data_frame_size = 0; - int risc_buffer_size = 0; - - if (out->_is_running) { - pr_info("Video Channel is still running so return!\n"); - return 0; - } - - sram_ch = chan->sram_channels; - - out->is_60hz = dev->tvnorm & V4L2_STD_525_60; - - /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for - * channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - out->_is_running = 0; - out->_frame_count = 0; - out->_file_status = RESET_STATUS; - out->_lines_count = out->is_60hz ? 480 : 576; - out->_pixel_format = pixel_format; - out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? - (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - data_frame_size = out->is_60hz ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; - risc_buffer_size = out->is_60hz ? - NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; - - out->_is_running = 0; - out->_frame_count = 0; - out->_file_status = RESET_STATUS; - out->_lines_count = out->is_60hz ? 480 : 576; - out->_pixel_format = pixel_format; - out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? - (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - out->curpos = 0; - init_waitqueue_head(&out->waitq); - - err = cx25821_sram_channel_setup_upstream(dev, sram_ch, - out->_line_size, 0); - - /* setup fifo + format */ - cx25821_set_pixelengine(chan, sram_ch, out->_pixel_format); - - out->upstream_riscbuf_size = risc_buffer_size * 2; - out->upstream_databuf_size = data_frame_size * 2; - - /* Allocating buffers and prepare RISC program */ - err = cx25821_upstream_buffer_prepare(chan, sram_ch, out->_line_size); - if (err < 0) { - pr_err("%s: Failed to set up Video upstream buffers!\n", - dev->name); - goto error; - } - - cx25821_start_video_dma_upstream(chan, sram_ch); - - return 0; - -error: - cx25821_dev_unregister(dev); - - return err; -} diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.h b/drivers/media/pci/cx25821/cx25821-video-upstream.h deleted file mode 100644 index b6cf95f2d11b..000000000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors , - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#include -#include - -#define OUTPUT_FRMT_656 0 -#define OPEN_FILE_1 0 -#define NUM_PROGS 8 -#define NUM_FRAMES 2 -#define ODD_FIELD 0 -#define EVEN_FIELD 1 -#define TOP_OFFSET 0 -#define FIFO_DISABLE 0 -#define FIFO_ENABLE 1 -#define TEST_FRAMES 5 -#define END_OF_FILE 0 -#define IN_PROGRESS 1 -#define RESET_STATUS -1 -#define NUM_NO_OPS 5 - -/* PAL and NTSC line sizes and number of lines. */ -#define WIDTH_D1 720 -#define NTSC_LINES_PER_FRAME 480 -#define PAL_LINES_PER_FRAME 576 -#define PAL_LINE_SZ 1440 -#define Y422_LINE_SZ 1440 -#define Y411_LINE_SZ 1080 -#define NTSC_FIELD_HEIGHT 240 -#define NTSC_ODD_FLD_LINES 241 -#define PAL_FIELD_HEIGHT 288 - -#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) -#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) - -#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) -#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) - -#define RISC_WRITECR_INSTRUCTION_SIZE 16 -#define RISC_SYNC_INSTRUCTION_SIZE 4 -#define JUMP_INSTRUCTION_SIZE 12 -#define MAXSIZE_NO_OPS 36 -#define DWORD_SIZE 4 - -#define USE_RISC_NOOP_VIDEO 1 - -#ifdef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) - -#endif - -#ifndef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#endif diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index b3eb2dabb30b..25eba4ac4499 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -432,18 +432,6 @@ extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, const struct sram_channel *ch, unsigned int bpl, u32 risc); -extern int cx25821_vidupstream_init(struct cx25821_channel *chan, int pixel_format); -extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, - int channel_select); -extern int cx25821_write_frame(struct cx25821_channel *chan, - const char __user *data, size_t count); -extern void cx25821_free_mem_upstream(struct cx25821_channel *chan); -extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); -extern void cx25821_stop_upstream_video(struct cx25821_channel *chan); -extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); -extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc); extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, u32 format); -- cgit v1.2.3-70-g09d2 From b7bf15c43c90c13c15db23612409783db97f680a Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Fri, 8 Jun 2018 17:45:51 -0400 Subject: media: i2c: adv748x: csi2: set entity function to video interface bridge The ADV748x CSI-2 subdevices are HDMI/AFE to MIPI CSI-2 bridges. Signed-off-by: Steve Longerbeam Acked-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 820b44ed56a8..469be87a3761 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -284,7 +284,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) adv748x_csi2_set_virtual_channel(tx, 0); adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, - MEDIA_ENT_F_UNKNOWN, + MEDIA_ENT_F_VID_IF_BRIDGE, is_txa(tx) ? "txa" : "txb"); /* Ensure that matching is based upon the endpoint fwnodes */ -- cgit v1.2.3-70-g09d2 From f91b84171b618dfd565b10c1a7feead0af869ba5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 11 Jun 2018 04:32:20 -0400 Subject: media: bt8xx: bttv: fix spelling mistake: "culpit" -> "culprit" Trivial fix to spelling mistake in pr_notice message text Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index de3f44b8dec6..cf05e11da01b 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3511,7 +3511,7 @@ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) } pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n", btv->c.nr); - pr_notice("%d: Lets try to catch the culpit red-handed ...\n", + pr_notice("%d: Lets try to catch the culprit red-handed ...\n", btv->c.nr); dump_stack(); } -- cgit v1.2.3-70-g09d2 From a9c24b8977c216cd7a8bf5a9f9d0621668e49746 Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:01 -0400 Subject: media: vim2m: Remove surplus name initialization Name is already initialized by assignment from vim2m_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 065483e62db4..1393aa806462 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1020,7 +1020,6 @@ static int vim2m_probe(struct platform_device *pdev) } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vim2m_videodev.name); v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n", vfd->num); -- cgit v1.2.3-70-g09d2 From 0d961c8998d7e8ea6dd0e00a7fdc629a0a0753cd Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:02 -0400 Subject: media: ti-vpe: Remove surplus name initialization Name is already initialized by assignment from vpe_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index e395aa85c8ad..de968295ca7d 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2485,7 +2485,6 @@ static void vpe_fw_cb(struct platform_device *pdev) } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", vfd->num); } -- cgit v1.2.3-70-g09d2 From 4fb5288b1965f33c7c0056c55b3ec5d9d4a52044 Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:03 -0400 Subject: media: s5p-g2d: Remove surplus name initialization Name is already initialized by assignment from g2d_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 66aa8cf1d048..3735c204e9ac 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -702,7 +702,6 @@ static int g2d_probe(struct platform_device *pdev) goto rel_vdev; } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name); dev->vfd = vfd; v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", vfd->num); -- cgit v1.2.3-70-g09d2 From 0874f0295d4e7f61b7c2c8b3ce3a7c70727693bd Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:04 -0400 Subject: media: mx2: Remove surplus name initialization Name is already initialized by assignment from emmaprp_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mx2_emmaprp.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 5a8eff60e95f..2e64729134b2 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -934,7 +934,6 @@ static int emmaprp_probe(struct platform_device *pdev) vfd->v4l2_dev = &pcdev->v4l2_dev; video_set_drvdata(vfd, pcdev); - snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); pcdev->vfd = vfd; v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME " Device registered as /dev/video%d\n", vfd->num); -- cgit v1.2.3-70-g09d2 From 661e7e8178fba7bbaf0845326b36fd99887982d3 Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:05 -0400 Subject: media: m2m-deinterlace: Remove surplus name initialization Name is already initialized by assignment from deinterlace_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 1e4195144f39..3008892eb8dd 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -1040,7 +1040,6 @@ static int deinterlace_probe(struct platform_device *pdev) } video_set_drvdata(vfd, pcdev); - snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name); v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME " Device registered as /dev/video%d\n", vfd->num); -- cgit v1.2.3-70-g09d2 From a4367ff42dde5d24f5217edba7c54cd13e21b2b6 Mon Sep 17 00:00:00 2001 From: Anton Leontiev Date: Tue, 12 Jun 2018 06:11:06 -0400 Subject: media: rga: Remove surplus name initialization Name is already initialized by assignment from rga_videodev. Signed-off-by: Anton Leontiev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index d508a8ba6f89..e8dffca669a1 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -882,7 +882,6 @@ static int rga_probe(struct platform_device *pdev) vfd->v4l2_dev = &rga->v4l2_dev; video_set_drvdata(vfd, rga); - snprintf(vfd->name, sizeof(vfd->name), "%s", rga_videodev.name); rga->vfd = vfd; platform_set_drvdata(pdev, rga); -- cgit v1.2.3-70-g09d2 From 34dbb848d5e47f0a16abeb40885f93dd43045ff1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jun 2018 09:22:51 -0400 Subject: media: mem2mem: Remove excessive try_run call If there is a schedulable job, v4l2_m2m_try_schedule() calls v4l2_m2m_try_run(). This makes the unconditional v4l2_m2m_try_run() called by v4l2_m2m_job_finish superfluous. Remove it. Fixes: 7f98639def42 ("V4L/DVB: add memory-to-memory device helper framework for videobuf") Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index c4f963d96a79..5f9cd5b74cda 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -339,7 +339,6 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, * allow more than one job on the job_queue per instance, each has * to be scheduled separately after the previous one finishes. */ v4l2_m2m_try_schedule(m2m_ctx); - v4l2_m2m_try_run(m2m_dev); } EXPORT_SYMBOL(v4l2_m2m_job_finish); -- cgit v1.2.3-70-g09d2 From 7534122d60066a50859c422ced4636deb23760bf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 12 Jun 2018 05:42:00 -0400 Subject: media: cx18: remove redundant zero check on retval The check for a zero retval is redundant as all paths that lead to this point have set retval to an error return value that is non-zero. Remove the redundant check. Detected by CoverityScan, CID#102589 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-driver.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 8f314ca320c7..0c389a3fb4e5 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -1134,8 +1134,6 @@ free_mem: free_workqueues: destroy_workqueue(cx->in_work_queue); err: - if (retval == 0) - retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); v4l2_device_unregister(&cx->v4l2_dev); -- cgit v1.2.3-70-g09d2 From 6c8871baebc781f27ec9ccc1324afd9802941b11 Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Mon, 11 Jun 2018 03:41:58 -0400 Subject: media: cx88: add error handling for snd_ctl_add When snd_ctl_add fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling snd_ctl_add. Signed-off-by: Zhouyang Jia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-alsa.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index e5c3387cd1e8..89a65478ae36 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -962,8 +962,11 @@ static int cx88_audio_initdev(struct pci_dev *pci, goto error; /* If there's a wm8775 then add a Line-In ALC switch */ - if (core->sd_wm8775) - snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); + if (core->sd_wm8775) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); + if (err < 0) + goto error; + } strcpy(card->driver, "CX88x"); sprintf(card->shortname, "Conexant CX%x", pci->device); -- cgit v1.2.3-70-g09d2 From e95d7c6eb94c634852eaa5ff4caf3db05b5d2e86 Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Mon, 11 Jun 2018 00:39:20 -0400 Subject: media: tm6000: add error handling for dvb_register_adapter When dvb_register_adapter fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling dvb_register_adapter. Signed-off-by: Zhouyang Jia [hans.verkuil@cisco.com: use pr_err and fix typo: adater -> adapter] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-dvb.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index c811fc6cf48a..3a4e545c6037 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -266,6 +266,11 @@ static int register_dvb(struct tm6000_core *dev) ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", THIS_MODULE, &dev->udev->dev, adapter_nr); + if (ret < 0) { + pr_err("tm6000: couldn't register the adapter!\n"); + goto err; + } + dvb->adapter.priv = dev; if (dvb->frontend) { -- cgit v1.2.3-70-g09d2 From 1946117b8f135a62eea5cfa18be63b1741174b9f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 5 Jun 2018 00:50:46 -0400 Subject: media: venus: keep resolution when adjusting format When checking a format for validity, the resolution is reset to 1280x720 whenever the pixel format is not supported. This behavior can mislead user-space into believing that this is the only resolution supported, and looks strange considering that if we try/set the same format with just the pixel format changed to a valid one, the call will this time succeed without altering the resolution. Resolution is managed independently of the pixel format, so remove this reset. Signed-off-by: Alexandre Courbot Acked-by: Stanimir Varbanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 2 -- drivers/media/platform/qcom/venus/venc.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 49bbd1861d3a..f89a91d43cc9 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -173,8 +173,6 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } pixmp->width = clamp(pixmp->width, inst->cap_width.min, diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6b2ce479584e..11dafc7848c5 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -297,8 +297,6 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } pixmp->width = clamp(pixmp->width, inst->cap_width.min, -- cgit v1.2.3-70-g09d2 From 9aecc03555825a79a8a4ca45199cb866e8684623 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 1 Jun 2018 15:49:51 -0400 Subject: media: rockchip/rga: Fix broken .start_streaming Currently, rga_buf_start_streaming() is expecting pm_runtime_get_sync to return zero on success, which is wrong. As per the documentation, pm_runtime_get_sync increments the device's usage counter and return its result. This means it will typically return a positive integer on success and a negative error code. Therefore, rockchip-rga driver is currently unusable failing to start_streaming in most cases. Fix it and while here, cleanup the buffer return-to-core logic. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Signed-off-by: Ezequiel Garcia Reviewed-by: Jacob Chen [hans.verkuil@cisco.com: fix line over 80 cols warning] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga-buf.c | 45 ++++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index fa1ba98c96dc..356821c2dacf 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -64,43 +64,44 @@ static void rga_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static void rga_buf_return_buffers(struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct rga_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + break; + v4l2_m2m_buf_done(vbuf, state); + } +} + static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) { struct rga_ctx *ctx = vb2_get_drv_priv(q); struct rockchip_rga *rga = ctx->rga; - int ret, i; + int ret; ret = pm_runtime_get_sync(rga->dev); - - if (!ret) - return 0; - - for (i = 0; i < q->num_buffers; ++i) { - if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]), - VB2_BUF_STATE_QUEUED); - } + if (ret < 0) { + rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED); + return ret; } - return ret; + return 0; } static void rga_buf_stop_streaming(struct vb2_queue *q) { struct rga_ctx *ctx = vb2_get_drv_priv(q); struct rockchip_rga *rga = ctx->rga; - struct vb2_v4l2_buffer *vbuf; - - for (;;) { - if (V4L2_TYPE_IS_OUTPUT(q->type)) - vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - else - vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!vbuf) - break; - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - } + rga_buf_return_buffers(q, VB2_BUF_STATE_ERROR); pm_runtime_put(rga->dev); } -- cgit v1.2.3-70-g09d2 From 1b97e5275ba6ef862a09f8b993fa3e8b373dfe9b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 1 Jun 2018 15:49:52 -0400 Subject: media: rockchip/rga: Remove unrequired wait in .job_abort As per the documentation, job_abort is not required to wait until the current job finishes. It is redundant to do so, as the core will perform the wait operation. Remove the wait infrastructure completely. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacob Chen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 13 +------------ drivers/media/platform/rockchip/rga/rga.h | 2 -- 2 files changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e8dffca669a1..8ace1873202a 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -41,14 +41,7 @@ module_param(debug, int, 0644); static void job_abort(void *prv) { - struct rga_ctx *ctx = prv; - struct rockchip_rga *rga = ctx->rga; - - if (!rga->curr) /* No job currently running */ - return; - - wait_event_timeout(rga->irq_queue, - !rga->curr, msecs_to_jiffies(RGA_TIMEOUT)); + /* Can't do anything rational here */ } static void device_run(void *prv) @@ -104,8 +97,6 @@ static irqreturn_t rga_isr(int irq, void *prv) v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx); - - wake_up(&rga->irq_queue); } return IRQ_HANDLED; @@ -838,8 +829,6 @@ static int rga_probe(struct platform_device *pdev) spin_lock_init(&rga->ctrl_lock); mutex_init(&rga->mutex); - init_waitqueue_head(&rga->irq_queue); - ret = rga_parse_dt(rga); if (ret) dev_err(&pdev->dev, "Unable to parse OF data\n"); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5d43e7ea88af..72d8a159fa7b 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -86,8 +86,6 @@ struct rockchip_rga { /* ctrl parm lock */ spinlock_t ctrl_lock; - wait_queue_head_t irq_queue; - struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; -- cgit v1.2.3-70-g09d2 From d7e913cc58c5f6db5159c72e8f8d1f2be9e2fe2f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 14 Jun 2018 11:34:03 -0400 Subject: media: mem2mem: Remove unused v4l2_m2m_ops .lock/.unlock Commit f1a81afc98e3 ("[media] m2m: fix bad unlock balance") removed the last use of v4l2_m2m_ops.lock and v4l2_m2m_ops.unlock hooks. They are not actually used anymore. Remove them. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 26 ++++------------------ drivers/media/platform/m2m-deinterlace.c | 20 ++--------------- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 18 --------------- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 16 ------------- drivers/media/platform/mx2_emmaprp.c | 16 ------------- drivers/media/platform/sti/delta/delta-v4l2.c | 18 --------------- drivers/media/platform/ti-vpe/vpe.c | 19 ---------------- include/media/v4l2-mem2mem.h | 6 ----- 8 files changed, 6 insertions(+), 133 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index c7631e117dd3..b86d704ae10c 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1297,28 +1297,10 @@ static void coda_job_abort(void *priv) "Aborting task\n"); } -static void coda_lock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - - mutex_lock(&pcdev->dev_mutex); -} - -static void coda_unlock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - - mutex_unlock(&pcdev->dev_mutex); -} - static const struct v4l2_m2m_ops coda_m2m_ops = { .device_run = coda_device_run, .job_ready = coda_job_ready, .job_abort = coda_job_abort, - .lock = coda_lock, - .unlock = coda_unlock, }; static void set_default_params(struct coda_ctx *ctx) @@ -2092,9 +2074,9 @@ static int coda_open(struct file *file) INIT_LIST_HEAD(&ctx->buffer_meta_list); spin_lock_init(&ctx->buffer_meta_lock); - coda_lock(ctx); + mutex_lock(&dev->dev_mutex); list_add(&ctx->list, &dev->instances); - coda_unlock(ctx); + mutex_unlock(&dev->dev_mutex); v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", ctx->idx, ctx); @@ -2142,9 +2124,9 @@ static int coda_release(struct file *file) flush_work(&ctx->seq_end_work); } - coda_lock(ctx); + mutex_lock(&dev->dev_mutex); list_del(&ctx->list); - coda_unlock(ctx); + mutex_unlock(&dev->dev_mutex); if (ctx->dev->devtype->product == CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 3008892eb8dd..a566ec566cec 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -181,20 +181,6 @@ static void deinterlace_job_abort(void *priv) v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); } -static void deinterlace_lock(void *priv) -{ - struct deinterlace_ctx *ctx = priv; - struct deinterlace_dev *pcdev = ctx->dev; - mutex_lock(&pcdev->dev_mutex); -} - -static void deinterlace_unlock(void *priv) -{ - struct deinterlace_ctx *ctx = priv; - struct deinterlace_dev *pcdev = ctx->dev; - mutex_unlock(&pcdev->dev_mutex); -} - static void dma_callback(void *data) { struct deinterlace_ctx *curr_ctx = data; @@ -956,9 +942,9 @@ static __poll_t deinterlace_poll(struct file *file, struct deinterlace_ctx *ctx = file->private_data; __poll_t ret; - deinterlace_lock(ctx); + mutex_lock(&ctx->dev->dev_mutex); ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - deinterlace_unlock(ctx); + mutex_unlock(&ctx->dev->dev_mutex); return ret; } @@ -992,8 +978,6 @@ static const struct v4l2_m2m_ops m2m_ops = { .device_run = deinterlace_device_run, .job_ready = deinterlace_job_ready, .job_abort = deinterlace_job_abort, - .lock = deinterlace_lock, - .unlock = deinterlace_unlock, }; static int deinterlace_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 86f0a7134365..5523edadb86c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1411,28 +1411,10 @@ int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) return 0; } -static void m2mops_vdec_lock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - mutex_lock(&ctx->dev->dev_mutex); -} - -static void m2mops_vdec_unlock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - mutex_unlock(&ctx->dev->dev_mutex); -} - const struct v4l2_m2m_ops mtk_vdec_m2m_ops = { .device_run = m2mops_vdec_device_run, .job_ready = m2mops_vdec_job_ready, .job_abort = m2mops_vdec_job_abort, - .lock = m2mops_vdec_lock, - .unlock = m2mops_vdec_unlock, }; static const struct vb2_ops mtk_vdec_vb2_ops = { diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 1b1a28abbf1f..6ad408514a99 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -1181,26 +1181,10 @@ static void m2mops_venc_job_abort(void *priv) ctx->state = MTK_STATE_ABORT; } -static void m2mops_venc_lock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mutex_lock(&ctx->dev->dev_mutex); -} - -static void m2mops_venc_unlock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mutex_unlock(&ctx->dev->dev_mutex); -} - const struct v4l2_m2m_ops mtk_venc_m2m_ops = { .device_run = m2mops_venc_device_run, .job_ready = m2mops_venc_job_ready, .job_abort = m2mops_venc_job_abort, - .lock = m2mops_venc_lock, - .unlock = m2mops_venc_unlock, }; void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 2e64729134b2..83b9db895a64 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -250,20 +250,6 @@ static void emmaprp_job_abort(void *priv) v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); } -static void emmaprp_lock(void *priv) -{ - struct emmaprp_ctx *ctx = priv; - struct emmaprp_dev *pcdev = ctx->dev; - mutex_lock(&pcdev->dev_mutex); -} - -static void emmaprp_unlock(void *priv) -{ - struct emmaprp_ctx *ctx = priv; - struct emmaprp_dev *pcdev = ctx->dev; - mutex_unlock(&pcdev->dev_mutex); -} - static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) { dprintk(pcdev, @@ -885,8 +871,6 @@ static const struct video_device emmaprp_videodev = { static const struct v4l2_m2m_ops m2m_ops = { .device_run = emmaprp_device_run, .job_abort = emmaprp_job_abort, - .lock = emmaprp_lock, - .unlock = emmaprp_unlock, }; static int emmaprp_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 232d508c5b66..0b42acd4e3a6 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -339,22 +339,6 @@ static void register_decoders(struct delta_dev *delta) } } -static void delta_lock(void *priv) -{ - struct delta_ctx *ctx = priv; - struct delta_dev *delta = ctx->dev; - - mutex_lock(&delta->lock); -} - -static void delta_unlock(void *priv) -{ - struct delta_ctx *ctx = priv; - struct delta_dev *delta = ctx->dev; - - mutex_unlock(&delta->lock); -} - static int delta_open_decoder(struct delta_ctx *ctx, u32 streamformat, u32 pixelformat, const struct delta_dec **pdec) { @@ -1099,8 +1083,6 @@ static const struct v4l2_m2m_ops delta_m2m_ops = { .device_run = delta_device_run, .job_ready = delta_job_ready, .job_abort = delta_job_abort, - .lock = delta_lock, - .unlock = delta_unlock, }; /* diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index de968295ca7d..d70871d0ad2d 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -953,23 +953,6 @@ static void job_abort(void *priv) ctx->aborting = 1; } -/* - * Lock access to the device - */ -static void vpe_lock(void *priv) -{ - struct vpe_ctx *ctx = priv; - struct vpe_dev *dev = ctx->dev; - mutex_lock(&dev->dev_mutex); -} - -static void vpe_unlock(void *priv) -{ - struct vpe_ctx *ctx = priv; - struct vpe_dev *dev = ctx->dev; - mutex_unlock(&dev->dev_mutex); -} - static void vpe_dump_regs(struct vpe_dev *dev) { #define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) @@ -2434,8 +2417,6 @@ static const struct v4l2_m2m_ops m2m_ops = { .device_run = device_run, .job_ready = job_ready, .job_abort = job_abort, - .lock = vpe_lock, - .unlock = vpe_unlock, }; static int vpe_runtime_get(struct platform_device *pdev) diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 3d07ba3a8262..8f4b208cfee7 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -40,17 +40,11 @@ * v4l2_m2m_job_finish() (as if the transaction ended normally). * This function does not have to (and will usually not) wait * until the device enters a state when it can be stopped. - * @lock: optional. Define a driver's own lock callback, instead of using - * &v4l2_m2m_ctx->q_lock. - * @unlock: optional. Define a driver's own unlock callback, instead of - * using &v4l2_m2m_ctx->q_lock. */ struct v4l2_m2m_ops { void (*device_run)(void *priv); int (*job_ready)(void *priv); void (*job_abort)(void *priv); - void (*lock)(void *priv); - void (*unlock)(void *priv); }; struct v4l2_m2m_dev; -- cgit v1.2.3-70-g09d2 From aed19754c491b336cb40bbd3f072b0e9e1397200 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 14 Jun 2018 11:34:05 -0400 Subject: media: rcar_vpu: Drop unneeded job_ready It is not required to implement job_ready(). Since this one is dummy, we can drop it. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_jpu.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 8b44a849ab41..690631747c5e 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1494,11 +1494,6 @@ static void jpu_device_run(void *priv) spin_unlock_irqrestore(&ctx->jpu->lock, flags); } -static int jpu_job_ready(void *priv) -{ - return 1; -} - static void jpu_job_abort(void *priv) { struct jpu_ctx *ctx = priv; @@ -1510,7 +1505,6 @@ static void jpu_job_abort(void *priv) static const struct v4l2_m2m_ops jpu_m2m_ops = { .device_run = jpu_device_run, - .job_ready = jpu_job_ready, .job_abort = jpu_job_abort, }; -- cgit v1.2.3-70-g09d2 From 1831af092308aa5a59ae61e47494e441c8be6b93 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sun, 10 Jun 2018 16:43:02 -0400 Subject: media: Revert "[media] tvp5150: fix pad format frame height" This reverts commit 0866df8dffd514185bfab0d205db76e4c02cf1e4. The v4l uAPI documentation [0] makes clear that in the case of interlaced video (i.e: field is V4L2_FIELD_ALTERNATE) the height refers to the number of lines in the field and not the number of lines in the full frame (which is twice the field height for interlaced formats). So the original height calculation was correct, and it shouldn't had been changed by the mentioned commit. [0]:https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/subdev-formats.html Fixes: 0866df8dffd5 ("[media] tvp5150: fix pad format frame height") Signed-off-by: Javier Martinez Canillas Cc: # for v4.12 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index b162c2fe62c3..76e6bed5a1da 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -872,7 +872,7 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, f = &format->format; f->width = decoder->rect.width; - f->height = decoder->rect.height; + f->height = decoder->rect.height / 2; f->code = MEDIA_BUS_FMT_UYVY8_2X8; f->field = V4L2_FIELD_ALTERNATE; -- cgit v1.2.3-70-g09d2 From e1a98c163eb276b5b5e1bece560ed7f0b9eb3b49 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Jun 2018 09:19:46 -0400 Subject: media: media.h: remove __NEED_MEDIA_LEGACY_API The __NEED_MEDIA_LEGACY_API define is 1) ugly and 2) dangerous since it is all too easy for drivers to define it to get hold of legacy defines. Instead just define what we need in media-device.c which is the only place where we need the legacy define (MEDIA_ENT_T_DEVNODE_UNKNOWN). Signed-off-by: Hans Verkuil Acked-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 13 ++++++++++--- include/uapi/linux/media.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index ae59c3177555..47bb2254fbfd 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -16,9 +16,6 @@ * GNU General Public License for more details. */ -/* We need to access legacy defines from linux/media.h */ -#define __NEED_MEDIA_LEGACY_API - #include #include #include @@ -35,6 +32,16 @@ #ifdef CONFIG_MEDIA_CONTROLLER +/* + * Legacy defines from linux/media.h. This is the only place we need this + * so we just define it here. The media.h header doesn't expose it to the + * kernel to prevent it from being used by drivers, but here (and only here!) + * we need it to handle the legacy behavior. + */ +#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff +#define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \ + MEDIA_ENT_SUBTYPE_MASK) + /* ----------------------------------------------------------------------------- * Userspace API */ diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index c7e9a5cba24e..86c7dcc9cba3 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -348,7 +348,7 @@ struct media_v2_topology { #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) #define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) -#if !defined(__KERNEL__) || defined(__NEED_MEDIA_LEGACY_API) +#ifndef __KERNEL__ /* * Legacy symbols used to avoid userspace compilation breakages. -- cgit v1.2.3-70-g09d2 From 5f9e711b75a51b96c05f61a93b3f7c0d79dc20e2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 May 2018 16:59:00 -0400 Subject: media: smiapp: Support the "rotation" property Use the "rotation" property to tell that the sensor is mounted upside down. This reverses the behaviour of the VFLIP and HFLIP controls as well as the pixel order. Signed-off-by: Sakari Ailus Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index e1f8208581aa..e9e0f21efc2a 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2764,6 +2764,7 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) struct v4l2_fwnode_endpoint *bus_cfg; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); + u32 rotation; int i; int rval; @@ -2800,6 +2801,21 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) dev_dbg(dev, "lanes %u\n", hwcfg->lanes); + rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); + if (!rval) { + switch (rotation) { + case 180: + hwcfg->module_board_orient = + SMIAPP_MODULE_BOARD_ORIENT_180; + /* Fall through */ + case 0: + break; + default: + dev_err(dev, "invalid rotation %u\n", rotation); + goto out_err; + } + } + /* NVM size is not mandatory */ fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size); -- cgit v1.2.3-70-g09d2 From ce85705a2abb4324e18ded9d6df2b278b952edb6 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 18 Jun 2018 06:29:17 -0400 Subject: media: ov5640: add HFLIP/VFLIP controls support Add HFLIP/VFLIP controls support by setting registers REG21/REG20. Useless values in hardcoded mode sequences are removed and remaining binning values are now set after mode sequence being set. Note that due to BSI (Back Side Illuminated) technology, image capture is physically mirrored, mirror logic is so inversed in REG21 register to cancel this effect. Signed-off-by: Hugues Fruchet Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 103 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index f6e40cc9745c..41039e535499 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -64,6 +64,7 @@ #define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e +#define OV5640_REG_TIMING_TC_REG20 0x3820 #define OV5640_REG_TIMING_TC_REG21 0x3821 #define OV5640_REG_AEC_CTRL00 0x3a00 #define OV5640_REG_AEC_B50_STEP 0x3a08 @@ -199,6 +200,8 @@ struct ov5640_ctrls { struct v4l2_ctrl *contrast; struct v4l2_ctrl *hue; struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; }; struct ov5640_dev { @@ -341,7 +344,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -360,7 +363,7 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -379,7 +382,7 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -399,7 +402,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -418,7 +421,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -437,7 +440,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -456,7 +459,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -475,7 +478,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -494,7 +497,7 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -513,7 +516,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -532,7 +535,7 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -551,7 +554,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -571,7 +574,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, @@ -591,7 +594,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, @@ -611,7 +614,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -644,7 +647,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -673,10 +676,9 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { }; static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -1340,6 +1342,27 @@ static int ov5640_binning_on(struct ov5640_dev *sensor) return temp ? 1 : 0; } +static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable) +{ + int ret; + + /* + * TIMING TC REG21: + * - [0]: Horizontal binning enable + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, + BIT(0), enable ? BIT(0) : 0); + if (ret) + return ret; + /* + * TIMING TC REG20: + * - [0]: Undocumented, but hardcoded init sequences + * are always setting REG21/REG20 bit 0 to same value... + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20, + BIT(0), enable ? BIT(0) : 0); +} + static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) { struct i2c_client *client = sensor->i2c_client; @@ -1640,6 +1663,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, if (ret < 0) return ret; + ret = ov5640_set_binning(sensor, dn_mode != SCALING); + if (ret < 0) + return ret; ret = ov5640_set_ae_target(sensor, sensor->ae_target); if (ret < 0) return ret; @@ -2193,6 +2219,37 @@ static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) BIT(2) : 0); } +static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value) +{ + /* + * Sensor is a BSI (Back Side Illuminated) one, + * so image captured is physically mirrored. + * This is why mirror logic is inversed in + * order to cancel this mirror effect. + */ + + /* + * TIMING TC REG21: + * - [2]: ISP mirror + * - [1]: Sensor mirror + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, + BIT(2) | BIT(1), + (!value) ? (BIT(2) | BIT(1)) : 0); +} + +static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) +{ + /* + * TIMING TC REG20: + * - [2]: ISP vflip + * - [1]: Sensor vflip + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20, + BIT(2) | BIT(1), + value ? (BIT(2) | BIT(1)) : 0); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2264,6 +2321,12 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_POWER_LINE_FREQUENCY: ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val); break; + case V4L2_CID_HFLIP: + ret = ov5640_set_ctrl_hflip(sensor, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov5640_set_ctrl_vflip(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2325,6 +2388,10 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops, -- cgit v1.2.3-70-g09d2 From c3f3ba3e6f78ea2159fca284e18840c6b3c1bfb6 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Mon, 18 Jun 2018 06:29:19 -0400 Subject: media: ov5640: add support of module orientation Add support of module being physically mounted upside down. In this case, mirror and flip are enabled to fix captured images orientation. [Sakari Ailus: Use dev_fwnode() instead of accessing device's of_node] Signed-off-by: Hugues Fruchet Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 41039e535499..9e1078632375 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -215,6 +215,7 @@ struct ov5640_dev { struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; struct gpio_desc *pwdn_gpio; + bool upside_down; /* lock to protect all members below */ struct mutex lock; @@ -2222,6 +2223,8 @@ static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value) { /* + * If sensor is mounted upside down, mirror logic is inversed. + * * Sensor is a BSI (Back Side Illuminated) one, * so image captured is physically mirrored. * This is why mirror logic is inversed in @@ -2235,11 +2238,14 @@ static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value) */ return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, BIT(2) | BIT(1), - (!value) ? (BIT(2) | BIT(1)) : 0); + (!(value ^ sensor->upside_down)) ? + (BIT(2) | BIT(1)) : 0); } static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) { + /* If sensor is mounted upside down, flip logic is inversed */ + /* * TIMING TC REG20: * - [2]: ISP vflip @@ -2247,7 +2253,8 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) */ return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20, BIT(2) | BIT(1), - value ? (BIT(2) | BIT(1)) : 0); + (value ^ sensor->upside_down) ? + (BIT(2) | BIT(1)) : 0); } static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -2625,6 +2632,7 @@ static int ov5640_probe(struct i2c_client *client, struct fwnode_handle *endpoint; struct ov5640_dev *sensor; struct v4l2_mbus_framefmt *fmt; + u32 rotation; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); @@ -2650,6 +2658,22 @@ static int ov5640_probe(struct i2c_client *client, sensor->ae_target = 52; + /* optional indication of physical rotation of sensor */ + ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", + &rotation); + if (!ret) { + switch (rotation) { + case 180: + sensor->upside_down = true; + /* fall through */ + case 0: + break; + default: + dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n", + rotation); + } + } + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!endpoint) { -- cgit v1.2.3-70-g09d2 From 0b964d183cbf3f95a062ad9f3eec87ffa2790558 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:18 -0400 Subject: media: ov772x: allow i2c controllers without I2C_FUNC_PROTOCOL_MANGLING The ov772x driver only works when the i2c controller have I2C_FUNC_PROTOCOL_MANGLING. However, many i2c controller drivers don't support it. The reason that the ov772x requires I2C_FUNC_PROTOCOL_MANGLING is that it doesn't support repeated starts. This changes the reading ov772x register method so that it doesn't require I2C_FUNC_PROTOCOL_MANGLING by calling two separated i2c messages. Cc: Laurent Pinchart Cc: Hans Verkuil Cc: Wolfram Sang Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index e2550708abc8..b6223bfc022b 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -542,9 +542,19 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) return container_of(sd, struct ov772x_priv, subdev); } -static inline int ov772x_read(struct i2c_client *client, u8 addr) +static int ov772x_read(struct i2c_client *client, u8 addr) { - return i2c_smbus_read_byte_data(client, addr); + int ret; + u8 val; + + ret = i2c_master_send(client, &addr, 1); + if (ret < 0) + return ret; + ret = i2c_master_recv(client, &val, 1); + if (ret < 0) + return ret; + + return val; } static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) @@ -1255,13 +1265,11 @@ static int ov772x_probe(struct i2c_client *client, return -EINVAL; } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_PROTOCOL_MANGLING)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, - "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n"); + "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n"); return -EIO; } - client->flags |= I2C_CLIENT_SCCB; priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) -- cgit v1.2.3-70-g09d2 From 30f3b17eaf4913e9e56be15915ce57aae69db701 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:19 -0400 Subject: media: ov772x: add checks for register read errors This change adds checks for register read errors and returns correct error code. Cc: Laurent Pinchart Cc: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index b6223bfc022b..3fdbe644648a 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1146,7 +1146,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, static int ov772x_video_probe(struct ov772x_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - u8 pid, ver; + int pid, ver, midh, midl; const char *devname; int ret; @@ -1156,7 +1156,11 @@ static int ov772x_video_probe(struct ov772x_priv *priv) /* Check and show product ID and manufacturer ID. */ pid = ov772x_read(client, PID); + if (pid < 0) + return pid; ver = ov772x_read(client, VER); + if (ver < 0) + return ver; switch (VERSION(pid, ver)) { case OV7720: @@ -1172,13 +1176,17 @@ static int ov772x_video_probe(struct ov772x_priv *priv) goto done; } + midh = ov772x_read(client, MIDH); + if (midh < 0) + return midh; + midl = ov772x_read(client, MIDL); + if (midl < 0) + return midl; + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, - pid, - ver, - ov772x_read(client, MIDH), - ov772x_read(client, MIDL)); + devname, pid, ver, midh, midl); + ret = v4l2_ctrl_handler_setup(&priv->hdl); done: -- cgit v1.2.3-70-g09d2 From 4b610d6d03eed17a035eba49826d1f47baf83396 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:20 -0400 Subject: media: ov772x: add media controller support Create a source pad and set the media controller type to the sensor. Cc: Laurent Pinchart Cc: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 3fdbe644648a..bb5327f4cb48 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -424,6 +424,9 @@ struct ov772x_priv { /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ unsigned short band_filter; unsigned int fps; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pad; +#endif }; /* @@ -1316,16 +1319,26 @@ static int ov772x_probe(struct i2c_client *client, if (ret < 0) goto error_gpio_put; +#ifdef CONFIG_MEDIA_CONTROLLER + priv->pad.flags = MEDIA_PAD_FL_SOURCE; + priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); + if (ret < 0) + goto error_gpio_put; +#endif + priv->cfmt = &ov772x_cfmts[0]; priv->win = &ov772x_win_sizes[0]; priv->fps = 15; ret = v4l2_async_register_subdev(&priv->subdev); if (ret) - goto error_gpio_put; + goto error_entity_cleanup; return 0; +error_entity_cleanup: + media_entity_cleanup(&priv->subdev.entity); error_gpio_put: if (priv->pwdn_gpio) gpiod_put(priv->pwdn_gpio); @@ -1341,6 +1354,7 @@ static int ov772x_remove(struct i2c_client *client) { struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); + media_entity_cleanup(&priv->subdev.entity); clk_put(priv->clk); if (priv->pwdn_gpio) gpiod_put(priv->pwdn_gpio); -- cgit v1.2.3-70-g09d2 From 40519d545824355fff7b94cf6e58efffe2908e7d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:21 -0400 Subject: media: ov772x: use generic names for reset and powerdown gpios The ov772x driver uses "rstb-gpios" and "pwdn-gpios" for reset and powerdown pins. However, using generic names for these gpios is preferred. ("reset-gpios" and "powerdown-gpios" respectively) There is only one mainline user for these gpios, so rename to generic names. Cc: Laurent Pinchart Cc: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-migor/setup.c | 5 +++-- drivers/media/i2c/ov772x.c | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 3d7d0046cf49..3849b89c8f80 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -351,8 +351,9 @@ static struct platform_device migor_ceu_device = { static struct gpiod_lookup_table ov7725_gpios = { .dev_id = "0-0021", .table = { - GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "pwdn", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "powerdown", + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "reset", GPIO_ACTIVE_LOW), }, }; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index bb5327f4cb48..97a65ce53efa 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -837,10 +837,10 @@ static int ov772x_power_on(struct ov772x_priv *priv) * available to handle this cleanly, request the GPIO temporarily * to avoid conflicts. */ - priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb", + priv->rstb_gpio = gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + dev_info(&client->dev, "Unable to get GPIO \"reset\""); return PTR_ERR(priv->rstb_gpio); } @@ -1307,10 +1307,10 @@ static int ov772x_probe(struct i2c_client *client, goto error_ctrl_free; } - priv->pwdn_gpio = gpiod_get_optional(&client->dev, "pwdn", + priv->pwdn_gpio = gpiod_get_optional(&client->dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(priv->pwdn_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"pwdn\""); + dev_info(&client->dev, "Unable to get GPIO \"powerdown\""); ret = PTR_ERR(priv->pwdn_gpio); goto error_clk_put; } -- cgit v1.2.3-70-g09d2 From 89ce93fd5b75d515ca4f9df15ef1507bff47b203 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:22 -0400 Subject: media: ov772x: omit consumer ID when getting clock reference Currently the ov772x driver obtains a clock with a specific consumer ID. As there's a single clock for this driver, we could omit clock-names property in device tree by passing NULL as a consumer ID to clk_get(). Cc: Laurent Pinchart Cc: Hans Verkuil Suggested-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-migor/setup.c | 2 +- drivers/media/i2c/ov772x.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 3849b89c8f80..26543cc30d26 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -593,7 +593,7 @@ static int __init migor_devices_setup(void) } /* Add a clock alias for ov7725 xclk source. */ - clk_add_alias("xclk", "0-0021", "video_clk", NULL); + clk_add_alias(NULL, "0-0021", "video_clk", NULL); /* Register GPIOs for video sources. */ gpiod_add_lookup_table(&ov7725_gpios); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 97a65ce53efa..f939e28ecaaf 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1300,7 +1300,7 @@ static int ov772x_probe(struct i2c_client *client, if (priv->hdl.error) return priv->hdl.error; - priv->clk = clk_get(&client->dev, "xclk"); + priv->clk = clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&client->dev, "Unable to get xclk clock\n"); ret = PTR_ERR(priv->clk); -- cgit v1.2.3-70-g09d2 From c2cae8951772aff8355171882fcc8e4eb9f53840 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:23 -0400 Subject: media: ov772x: support device tree probing The ov772x driver currently only supports legacy platform data probe. This change enables device tree probing. Note that the platform data probe can select auto or manual edge control mode, but the device tree probling can only select auto edge control mode for now. [Sakari Ailus: Remove direct OF dependencies from device ID table] Cc: Laurent Pinchart Cc: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 62 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index f939e28ecaaf..b5ae88e69b94 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -749,13 +749,13 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: val = ctrl->val ? VFLIP_IMG : 0x00; priv->flag_vflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_VFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val ^= VFLIP_IMG; return ov772x_mask_set(client, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP: val = ctrl->val ? HFLIP_IMG : 0x00; priv->flag_hflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_HFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val ^= HFLIP_IMG; return ov772x_mask_set(client, COM3, HFLIP_IMG, val); case V4L2_CID_BAND_STOP_FILTER: @@ -914,19 +914,14 @@ static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, *win = ov772x_select_win(mf->width, mf->height); } -static int ov772x_set_params(struct ov772x_priv *priv, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) +static int ov772x_edgectrl(struct ov772x_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - struct v4l2_fract tpf; int ret; - u8 val; - /* Reset hardware. */ - ov772x_reset(client); + if (!priv->info) + return 0; - /* Edge Ctrl. */ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { /* * Manual Edge Control Mode. @@ -937,19 +932,19 @@ static int ov772x_set_params(struct ov772x_priv *priv, ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { /* @@ -961,15 +956,35 @@ static int ov772x_set_params(struct ov772x_priv *priv, EDGE_UPPER, OV772X_EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_LOWER, OV772X_EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; } + return 0; +} + +static int ov772x_set_params(struct ov772x_priv *priv, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + struct v4l2_fract tpf; + int ret; + u8 val; + + /* Reset hardware. */ + ov772x_reset(client); + + /* Edge Ctrl. */ + ret = ov772x_edgectrl(priv); + if (ret < 0) + return ret; + /* Format and window size. */ ret = ov772x_write(client, HSTART, win->rect.left >> 2); if (ret < 0) @@ -1020,9 +1035,9 @@ static int ov772x_set_params(struct ov772x_priv *priv, /* Set COM3. */ val = cfmt->com3; - if (priv->info->flags & OV772X_FLAG_VFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val |= VFLIP_IMG; - if (priv->info->flags & OV772X_FLAG_HFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val |= HFLIP_IMG; if (priv->flag_vflip) val ^= VFLIP_IMG; @@ -1271,8 +1286,9 @@ static int ov772x_probe(struct i2c_client *client, struct i2c_adapter *adapter = client->adapter; int ret; - if (!client->dev.platform_data) { - dev_err(&client->dev, "Missing ov772x platform data\n"); + if (!client->dev.of_node && !client->dev.platform_data) { + dev_err(&client->dev, + "Missing ov772x platform data for non-DT device\n"); return -EINVAL; } @@ -1370,9 +1386,17 @@ static const struct i2c_device_id ov772x_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov772x_id); +static const struct of_device_id ov772x_of_match[] = { + { .compatible = "ovti,ov7725", }, + { .compatible = "ovti,ov7720", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov772x_of_match); + static struct i2c_driver ov772x_i2c_driver = { .driver = { .name = "ov772x", + .of_match_table = ov772x_of_match, }, .probe = ov772x_probe, .remove = ov772x_remove, -- cgit v1.2.3-70-g09d2 From 34af7d920b766ffa582231ff3ce526d0c6179cd0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:24 -0400 Subject: media: ov772x: handle nested s_power() calls Depending on the v4l2 driver, calling s_power() could be nested. So the actual transitions between power saving mode and normal operation mode should only happen at the first power on and the last power off. This adds an s_power() nesting counter and updates the power state if the counter is modified from 0 to != 0 or from != 0 to 0. Cc: Laurent Pinchart Cc: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index b5ae88e69b94..4a30a4338445 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -424,6 +424,9 @@ struct ov772x_priv { /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ unsigned short band_filter; unsigned int fps; + /* lock to protect power_count */ + struct mutex lock; + int power_count; #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; #endif @@ -871,9 +874,26 @@ static int ov772x_power_off(struct ov772x_priv *priv) static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct ov772x_priv *priv = to_ov772x(sd); + int ret = 0; + + mutex_lock(&priv->lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (priv->power_count == !on) + ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv); + + if (!ret) { + /* Update the power count. */ + priv->power_count += on ? 1 : -1; + WARN(priv->power_count < 0, "Unbalanced power count\n"); + WARN(priv->power_count > 1, "Duplicated s_power call\n"); + } + + mutex_unlock(&priv->lock); - return on ? ov772x_power_on(priv) : - ov772x_power_off(priv); + return ret; } static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) @@ -1303,6 +1323,7 @@ static int ov772x_probe(struct i2c_client *client, return -ENOMEM; priv->info = client->dev.platform_data; + mutex_init(&priv->lock); v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); @@ -1313,8 +1334,10 @@ static int ov772x_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; + if (priv->hdl.error) { + ret = priv->hdl.error; + goto error_mutex_destroy; + } priv->clk = clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { @@ -1362,6 +1385,8 @@ error_clk_put: clk_put(priv->clk); error_ctrl_free: v4l2_ctrl_handler_free(&priv->hdl); +error_mutex_destroy: + mutex_destroy(&priv->lock); return ret; } @@ -1376,6 +1401,7 @@ static int ov772x_remove(struct i2c_client *client) gpiod_put(priv->pwdn_gpio); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); return 0; } -- cgit v1.2.3-70-g09d2 From 80dc2a49ba040b3ce09c13d5a7be1799bc75a166 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:25 -0400 Subject: media: ov772x: reconstruct s_frame_interval() This splits the s_frame_interval() in subdev video ops into selecting the frame interval and setting up the registers. This is a preparatory change to avoid accessing registers under power saving mode. Cc: Hans Verkuil Signed-off-by: Akinobu Mita Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 56 +++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 4a30a4338445..633a1b31b6d7 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -617,25 +617,16 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ov772x_set_frame_rate(struct ov772x_priv *priv, - struct v4l2_fract *tpf, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) +static unsigned int ov772x_select_fps(struct ov772x_priv *priv, + struct v4l2_fract *tpf) { - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - unsigned long fin = clk_get_rate(priv->clk); unsigned int fps = tpf->numerator ? tpf->denominator / tpf->numerator : tpf->denominator; unsigned int best_diff; - unsigned int fsize; - unsigned int pclk; unsigned int diff; unsigned int idx; unsigned int i; - u8 clkrc = 0; - u8 com4 = 0; - int ret; /* Approximate to the closest supported frame interval. */ best_diff = ~0L; @@ -646,7 +637,25 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, best_diff = diff; } } - fps = ov772x_frame_intervals[idx]; + + return ov772x_frame_intervals[idx]; +} + +static int ov772x_set_frame_rate(struct ov772x_priv *priv, + unsigned int fps, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + unsigned long fin = clk_get_rate(priv->clk); + unsigned int best_diff; + unsigned int fsize; + unsigned int pclk; + unsigned int diff; + unsigned int i; + u8 clkrc = 0; + u8 com4 = 0; + int ret; /* Use image size (with blankings) to calculate desired pixel clock. */ switch (cfmt->com7 & OFMT_MASK) { @@ -711,10 +720,6 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, if (ret < 0) return ret; - tpf->numerator = 1; - tpf->denominator = fps; - priv->fps = tpf->denominator; - return 0; } @@ -735,8 +740,20 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd, { struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_fract *tpf = &ival->interval; + unsigned int fps; + int ret; + + fps = ov772x_select_fps(priv, tpf); + + ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); + if (ret) + return ret; - return ov772x_set_frame_rate(priv, tpf, priv->cfmt, priv->win); + tpf->numerator = 1; + tpf->denominator = fps; + priv->fps = fps; + + return 0; } static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -993,7 +1010,6 @@ static int ov772x_set_params(struct ov772x_priv *priv, const struct ov772x_win_size *win) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - struct v4l2_fract tpf; int ret; u8 val; @@ -1075,9 +1091,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, goto ov772x_set_fmt_error; /* COM4, CLKRC: Set pixel clock and framerate. */ - tpf.numerator = 1; - tpf.denominator = priv->fps; - ret = ov772x_set_frame_rate(priv, &tpf, cfmt, win); + ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win); if (ret < 0) goto ov772x_set_fmt_error; -- cgit v1.2.3-70-g09d2 From 09e620c6af57ecc94411895eec78c5799a6e2a8d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:26 -0400 Subject: media: ov772x: use v4l2_ctrl to get current control value The ov772x driver provides three V4L2 controls and the current value of each control is saved as a variable in the private data structure. We don't need to keep track of the current value by ourself, if we use v4l2_ctrl returned from v4l2_ctrl_new_std() instead. This is a preparatory change to avoid accessing registers under power saving mode. This simplifies s_ctrl() by making it just return without saving the current control value in private area when it is called under power saving mode. Cc: Jacopo Mondi Cc: Laurent Pinchart Cc: Hans Verkuil Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 633a1b31b6d7..553c334c55b6 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -419,10 +419,10 @@ struct ov772x_priv { struct gpio_desc *rstb_gpio; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; - unsigned short flag_vflip:1; - unsigned short flag_hflip:1; + struct v4l2_ctrl *vflip_ctrl; + struct v4l2_ctrl *hflip_ctrl; /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ - unsigned short band_filter; + struct v4l2_ctrl *band_filter_ctrl; unsigned int fps; /* lock to protect power_count */ struct mutex lock; @@ -768,13 +768,11 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VFLIP: val = ctrl->val ? VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->val; if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val ^= VFLIP_IMG; return ov772x_mask_set(client, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP: val = ctrl->val ? HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->val; if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val ^= HFLIP_IMG; return ov772x_mask_set(client, COM3, HFLIP_IMG, val); @@ -794,8 +792,7 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) ret = ov772x_mask_set(client, BDBASE, 0xff, val); } - if (!ret) - priv->band_filter = ctrl->val; + return ret; } @@ -1075,9 +1072,9 @@ static int ov772x_set_params(struct ov772x_priv *priv, val |= VFLIP_IMG; if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val |= HFLIP_IMG; - if (priv->flag_vflip) + if (priv->vflip_ctrl->val) val ^= VFLIP_IMG; - if (priv->flag_hflip) + if (priv->hflip_ctrl->val) val ^= HFLIP_IMG; ret = ov772x_mask_set(client, @@ -1096,11 +1093,13 @@ static int ov772x_set_params(struct ov772x_priv *priv, goto ov772x_set_fmt_error; /* Set COM8. */ - if (priv->band_filter) { + if (priv->band_filter_ctrl->val) { + unsigned short band_filter = priv->band_filter_ctrl->val; + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, - 0xff, 256 - priv->band_filter); + 0xff, 256 - band_filter); if (ret < 0) goto ov772x_set_fmt_error; } @@ -1341,12 +1340,13 @@ static int ov772x_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); + priv->vflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + priv->hflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_BAND_STOP_FILTER, + 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; if (priv->hdl.error) { ret = priv->hdl.error; -- cgit v1.2.3-70-g09d2 From 95f5a45aea8800e011698fef1c4f535e9afb374a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:27 -0400 Subject: media: ov772x: avoid accessing registers under power saving mode The set_fmt() in subdev pad ops, the s_ctrl() for subdev control handler, and the s_frame_interval() in subdev video ops could be called when the device is under power saving mode. These callbacks for ov772x driver cause updating H/W registers that will fail under power saving mode. This avoids it by not apply any changes to H/W if the device is not powered up. Instead the changes will be restored right after power-up. Cc: Laurent Pinchart Cc: Hans Verkuil Signed-off-by: Akinobu Mita Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 79 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 553c334c55b6..d380e75db760 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -741,19 +741,30 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd, struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_fract *tpf = &ival->interval; unsigned int fps; - int ret; + int ret = 0; fps = ov772x_select_fps(priv, tpf); - ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); - if (ret) - return ret; + mutex_lock(&priv->lock); + /* + * If the device is not powered up by the host driver do + * not apply any changes to H/W at this time. Instead + * the frame rate will be restored right after power-up. + */ + if (priv->power_count > 0) { + ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); + if (ret) + goto error; + } tpf->numerator = 1; tpf->denominator = fps; priv->fps = fps; - return 0; +error: + mutex_unlock(&priv->lock); + + return ret; } static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -765,6 +776,16 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; u8 val; + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (priv->power_count == 0) + return 0; + switch (ctrl->id) { case V4L2_CID_VFLIP: val = ctrl->val ? VFLIP_IMG : 0x00; @@ -885,6 +906,10 @@ static int ov772x_power_off(struct ov772x_priv *priv) return 0; } +static int ov772x_set_params(struct ov772x_priv *priv, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win); + static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct ov772x_priv *priv = to_ov772x(sd); @@ -895,8 +920,20 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on) /* If the power count is modified from 0 to != 0 or from != 0 to 0, * update the power state. */ - if (priv->power_count == !on) - ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv); + if (priv->power_count == !on) { + if (on) { + ret = ov772x_power_on(priv); + /* + * Restore the format, the frame rate, and + * the controls + */ + if (!ret) + ret = ov772x_set_params(priv, priv->cfmt, + priv->win); + } else { + ret = ov772x_power_off(priv); + } + } if (!ret) { /* Update the power count. */ @@ -1163,7 +1200,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &format->format; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; - int ret; + int ret = 0; if (format->pad) return -EINVAL; @@ -1184,14 +1221,24 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, return 0; } - ret = ov772x_set_params(priv, cfmt, win); - if (ret < 0) - return ret; - + mutex_lock(&priv->lock); + /* + * If the device is not powered up by the host driver do + * not apply any changes to H/W at this time. Instead + * the format will be restored right after power-up. + */ + if (priv->power_count > 0) { + ret = ov772x_set_params(priv, cfmt, win); + if (ret < 0) + goto error; + } priv->win = win; priv->cfmt = cfmt; - return 0; +error: + mutex_unlock(&priv->lock); + + return ret; } static int ov772x_video_probe(struct ov772x_priv *priv) @@ -1201,7 +1248,7 @@ static int ov772x_video_probe(struct ov772x_priv *priv) const char *devname; int ret; - ret = ov772x_s_power(&priv->subdev, 1); + ret = ov772x_power_on(priv); if (ret < 0) return ret; @@ -1241,7 +1288,7 @@ static int ov772x_video_probe(struct ov772x_priv *priv) ret = v4l2_ctrl_handler_setup(&priv->hdl); done: - ov772x_s_power(&priv->subdev, 0); + ov772x_power_off(priv); return ret; } @@ -1340,6 +1387,8 @@ static int ov772x_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); + /* Use our mutex for the controls */ + priv->hdl.lock = &priv->lock; priv->vflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); priv->hflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, -- cgit v1.2.3-70-g09d2 From 7b9998c9361950040af1c6e7a914519e891052a1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:28 -0400 Subject: media: ov772x: make set_fmt() and s_frame_interval() return -EBUSY while streaming The ov772x driver is going to offer a V4L2 sub-device interface, so changing the output data format and the frame interval on this sub-device can be made anytime. However, these requests are preferred to fail while the video stream on the device is active. Cc: Laurent Pinchart Cc: Hans Verkuil Signed-off-by: Akinobu Mita Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index d380e75db760..9ff8e60c67b8 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -424,9 +424,10 @@ struct ov772x_priv { /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ struct v4l2_ctrl *band_filter_ctrl; unsigned int fps; - /* lock to protect power_count */ + /* lock to protect power_count and streaming */ struct mutex lock; int power_count; + int streaming; #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; #endif @@ -603,18 +604,28 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov772x_priv *priv = to_ov772x(sd); + int ret = 0; - if (!enable) { - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; - } + mutex_lock(&priv->lock); - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); + if (priv->streaming == enable) + goto done; - dev_dbg(&client->dev, "format %d, win %s\n", - priv->cfmt->code, priv->win->name); + ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, + enable ? 0 : SOFT_SLEEP_MODE); + if (ret) + goto done; - return 0; + if (enable) { + dev_dbg(&client->dev, "format %d, win %s\n", + priv->cfmt->code, priv->win->name); + } + priv->streaming = enable; + +done: + mutex_unlock(&priv->lock); + + return ret; } static unsigned int ov772x_select_fps(struct ov772x_priv *priv, @@ -743,9 +754,15 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd, unsigned int fps; int ret = 0; + mutex_lock(&priv->lock); + + if (priv->streaming) { + ret = -EBUSY; + goto error; + } + fps = ov772x_select_fps(priv, tpf); - mutex_lock(&priv->lock); /* * If the device is not powered up by the host driver do * not apply any changes to H/W at this time. Instead @@ -1222,6 +1239,12 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, } mutex_lock(&priv->lock); + + if (priv->streaming) { + ret = -EBUSY; + goto error; + } + /* * If the device is not powered up by the host driver do * not apply any changes to H/W at this time. Instead -- cgit v1.2.3-70-g09d2 From 795bce437f4ef9bdc116af1d7575c9f73f783a0c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 6 May 2018 10:19:29 -0400 Subject: media: ov772x: create subdevice device node Set the V4L2_SUBDEV_FL_HAS_DEVNODE flag for the subdevice so that the subdevice device node is created. Cc: Laurent Pinchart Cc: Hans Verkuil Signed-off-by: Akinobu Mita Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov772x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 9ff8e60c67b8..7158c31d8403 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1409,6 +1409,7 @@ static int ov772x_probe(struct i2c_client *client, mutex_init(&priv->lock); v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&priv->hdl, 3); /* Use our mutex for the controls */ priv->hdl.lock = &priv->lock; -- cgit v1.2.3-70-g09d2 From 6f3de8388efcede60a7aee04022c4d02719f44c8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 11:25:08 -0400 Subject: media: omap3isp: fix warning for !CONFIG_PM The final version of the COMPILE_TEST patch for this driver missed one warning about suspend/resume functions that can now appear on platforms that don't always set CONFIG_PM: drivers/media/platform/omap3isp/isp.c:1008:13: error: 'isp_resume_modules' defined but not used [-Werror=unused-function] static void isp_resume_modules(struct isp_device *isp) ^~~~~~~~~~~~~~~~~~ drivers/media/platform/omap3isp/isp.c:974:12: error: 'isp_suspend_modules' defined but not used [-Werror=unused-function] static int isp_suspend_modules(struct isp_device *isp) This marks the respective functions as __maybe_unused as an easy workaround. Fixes: 243131134be4 ("media: omap3isp: Allow it to build with COMPILE_TEST") Signed-off-by: Arnd Bergmann Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index f22cf351e3ee..a658c12eead1 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -971,7 +971,7 @@ static void isp_resume_module_pipeline(struct media_entity *me) * Returns 0 if suspend left in idle state all the submodules properly, * or returns 1 if a general Reset is required to suspend the submodules. */ -static int isp_suspend_modules(struct isp_device *isp) +static int __maybe_unused isp_suspend_modules(struct isp_device *isp) { unsigned long timeout; @@ -1005,7 +1005,7 @@ static int isp_suspend_modules(struct isp_device *isp) * isp_resume_modules - Resume ISP submodules. * @isp: OMAP3 ISP device */ -static void isp_resume_modules(struct isp_device *isp) +static void __maybe_unused isp_resume_modules(struct isp_device *isp) { omap3isp_stat_resume(&isp->isp_aewb); omap3isp_stat_resume(&isp->isp_af); -- cgit v1.2.3-70-g09d2 From 5b0a205466578432ce8faadd08f8d7ef895a180f Mon Sep 17 00:00:00 2001 From: Alan Chiang Date: Tue, 24 Apr 2018 22:12:08 -0400 Subject: media: dw9807: Add dw9807 vcm driver DW9807 is a 10 bit DAC from Dongwoon, designed for linear control of voice coil motor. This driver creates a V4L2 subdevice and provides control to set the desired focus. Signed-off-by: Alan Chiang Signed-off-by: Andy Yeh Reviewed-by: Jacopo Mondi Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 10 ++ drivers/media/i2c/Makefile | 1 + drivers/media/i2c/dw9807.c | 329 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+) create mode 100644 drivers/media/i2c/dw9807.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index ed6f068e7ce1..bd147aee7f80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4403,6 +4403,13 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/i2c/dw9714.c +DONGWOON DW9807 LENS VOICE COIL DRIVER +M: Sakari Ailus +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/dw9807.c + DOUBLETALK DRIVER M: "James R. Van Zandt" L: blinux-list@redhat.com diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1d5efef77e77..1cf91bce2613 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -336,6 +336,16 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9807 + tristate "DW9807 lens voice coil support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + ---help--- + This is a driver for the DW9807 camera lens voice coil. + DW9807 is a 10 bit DAC with 100mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 33cf2a415657..1d994658a9d4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9807) += dw9807.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o diff --git a/drivers/media/i2c/dw9807.c b/drivers/media/i2c/dw9807.c new file mode 100644 index 000000000000..8ba3920b6e2f --- /dev/null +++ b/drivers/media/i2c/dw9807.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DW9807_MAX_FOCUS_POS 1023 +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position. + */ +#define DW9807_FOCUS_STEPS 1 +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9807_CTRL_STEPS 16 +#define DW9807_CTRL_DELAY_US 1000 + +#define DW9807_CTL_ADDR 0x02 +/* + * DW9807 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + */ +#define DW9807_MSB_ADDR 0x03 +#define DW9807_LSB_ADDR 0x04 +#define DW9807_STATUS_ADDR 0x05 +#define DW9807_MODE_ADDR 0x06 +#define DW9807_RESONANCE_ADDR 0x07 + +#define MAX_RETRY 10 + +struct dw9807_device { + struct v4l2_ctrl_handler ctrls_vcm; + struct v4l2_subdev sd; + u16 current_val; +}; + +static inline struct dw9807_device *sd_to_dw9807_vcm( + struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9807_device, sd); +} + +static int dw9807_i2c_check(struct i2c_client *client) +{ + const char status_addr = DW9807_STATUS_ADDR; + char status_result; + int ret; + + ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); + if (ret < 0) { + dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", + ret); + return ret; + } + + ret = i2c_master_recv(client, &status_result, sizeof(status_result)); + if (ret < 0) { + dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", + ret); + return ret; + } + + return status_result; +} + +static int dw9807_set_dac(struct i2c_client *client, u16 data) +{ + const char tx_data[3] = { + DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) + }; + int val, ret; + + /* + * According to the datasheet, need to check the bus status before we + * write VCM position. This ensure that we really write the value + * into the register + */ + ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, + DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); + + if (ret || val < 0) { + if (ret) { + dev_warn(&client->dev, + "Cannot do the write operation because VCM is busy\n"); + } + + return ret ? -EBUSY : val; + } + + /* Write VCM position to registers */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, + "I2C write MSB fail ret=%d\n", ret); + + return ret; + } + + return 0; +} + +static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9807_device *dev_vcm = container_of(ctrl->handler, + struct dw9807_device, ctrls_vcm); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + dev_vcm->current_val = ctrl->val; + return dw9807_set_dac(client, ctrl->val); + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { + .s_ctrl = dw9807_set_ctrl, +}; + +static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rval; + + rval = pm_runtime_get_sync(sd->dev); + if (rval < 0) { + pm_runtime_put_noidle(sd->dev); + return rval; + } + + return 0; +} + +static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9807_int_ops = { + .open = dw9807_open, + .close = dw9807_close, +}; + +static const struct v4l2_subdev_ops dw9807_ops = { }; + +static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) +{ + v4l2_async_unregister_subdev(&dw9807_dev->sd); + v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); + media_entity_cleanup(&dw9807_dev->sd.entity); +} + +static int dw9807_init_controls(struct dw9807_device *dev_vcm) +{ + struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; + const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + v4l2_ctrl_handler_init(hdl, 1); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, + 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); + + dev_vcm->sd.ctrl_handler = hdl; + if (hdl->error) { + dev_err(&client->dev, "%s fail error: 0x%x\n", + __func__, hdl->error); + return hdl->error; + } + + return 0; +} + +static int dw9807_probe(struct i2c_client *client) +{ + struct dw9807_device *dw9807_dev; + int rval; + + dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), + GFP_KERNEL); + if (dw9807_dev == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); + dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9807_dev->sd.internal_ops = &dw9807_int_ops; + + rval = dw9807_init_controls(dw9807_dev); + if (rval) + goto err_cleanup; + + rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); + if (rval < 0) + goto err_cleanup; + + dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; + + rval = v4l2_async_register_subdev(&dw9807_dev->sd); + if (rval < 0) + goto err_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +err_cleanup: + dw9807_subdev_cleanup(dw9807_dev); + + return rval; +} + +static int dw9807_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + dw9807_subdev_cleanup(dw9807_dev); + + return 0; +} + +/* + * This function sets the vcm position, so it consumes least current + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; + int ret, val; + + for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); + val >= 0; val -= DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_once(dev, "%s I2C failure: %d", __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + /* Power down */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + return 0; +} + +/* + * This function sets the vcm position to the value set by the user + * through v4l2_ctrl_ops s_ctrl handler + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; + int ret, val; + + /* Power on */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; + val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; + val += DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_ratelimited(dev, "%s I2C failure: %d", + __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + return 0; +} + +static const struct of_device_id dw9807_of_table[] = { + { .compatible = "dongwoon,dw9807-vcm" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw9807_of_table); + +static const struct dev_pm_ops dw9807_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) + SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) +}; + +static struct i2c_driver dw9807_i2c_driver = { + .driver = { + .name = "dw9807", + .pm = &dw9807_pm_ops, + .of_match_table = dw9807_of_table, + }, + .probe_new = dw9807_probe, + .remove = dw9807_remove, +}; + +module_i2c_driver(dw9807_i2c_driver); + +MODULE_AUTHOR("Chiang, Alan "); +MODULE_DESCRIPTION("DW9807 VCM driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 2ec7debd44b49927a6e2861521994cc075a389ed Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 9 Jun 2018 08:22:45 -0400 Subject: media: omap3isp: zero-initialize the isp cam_xclk{a,b} initial data The struct clk_init_data init variable is declared in the isp_xclk_init() function so is an automatic variable allocated in the stack. But it's not explicitly zero-initialized, so some init fields are left uninitialized. This causes the data structure to have undefined values that may confuse the common clock framework when the clock is registered. For example, the uninitialized .flags field could have the CLK_IS_CRITICAL bit set, causing the framework to wrongly prepare the clk on registration. This leads to the isp_xclk_prepare() callback being called, which in turn calls to the omap3isp_get() function that increments the isp dev refcount. Since this omap3isp_get() call is unexpected, this leads to an unbalanced omap3isp_get() call that prevents the requested IRQ to be later enabled, due the refcount not being 0 when the correct omap3isp_get() call happens. Fixes: 9b28ee3c9122 ("[media] omap3isp: Use the common clock framework") Signed-off-by: Javier Martinez Canillas Reviewed-by: Sebastian Reichel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index a658c12eead1..03354d513311 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -300,7 +300,7 @@ static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) static int isp_xclk_init(struct isp_device *isp) { struct device_node *np = isp->dev->of_node; - struct clk_init_data init; + struct clk_init_data init = { 0 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) -- cgit v1.2.3-70-g09d2 From 30ed2b83343bd1e07884ca7355dac70d25ffc158 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 10 Jun 2018 11:42:01 -0400 Subject: media: s3c-camif: ignore -ENOIOCTLCMD from v4l2_subdev_call for s_power When the subdevice doesn't provide s_power core ops callback, the v4l2_subdev_call for s_power returns -ENOIOCTLCMD. If the subdevice doesn't have the special handling for its power saving mode, the s_power isn't required. So -ENOIOCTLCMD from the v4l2_subdev_call should be ignored. Cc: Hans Verkuil Signed-off-by: Akinobu Mita Acked-by: Sylwester Nawrocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-capture.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 9ab8e7ee2e1e..b1d9f3857d3d 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -117,6 +117,8 @@ static int sensor_set_power(struct camif_dev *camif, int on) if (camif->sensor.power_count == !on) err = v4l2_subdev_call(sensor->sd, core, s_power, on); + if (err == -ENOIOCTLCMD) + err = 0; if (!err) sensor->power_count += on ? 1 : -1; -- cgit v1.2.3-70-g09d2 From 22216ec41e919682c15345e95928f266e8ba6f9e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 10 Jun 2018 11:42:26 -0400 Subject: media: soc_camera: ov772x: correct setting of banding filter The banding filter ON/OFF is controlled via bit 5 of COM8 register. It is attempted to be enabled in ov772x_set_params() by the following line. ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); But this unexpectedly results disabling the banding filter, because the mask and set bits are exclusive. On the other hand, ov772x_s_ctrl() correctly sets the bit by: ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); The same fix was already applied to non-soc_camera version of ov772x driver in the commit commit a024ee14cd36 ("media: ov772x: correct setting of banding filter") Cc: Jacopo Mondi Cc: Laurent Pinchart Cc: Hans Verkuil Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/ov772x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 806383500313..14377af7c888 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -834,7 +834,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, * set COM8 */ if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, 0xff, 256 - priv->band_filter); -- cgit v1.2.3-70-g09d2 From 41cb1c739dcddf9905a5a3e3da9429b89cd5c616 Mon Sep 17 00:00:00 2001 From: Philipp Puschmann Date: Wed, 6 Jun 2018 05:11:38 -0400 Subject: media: ov5640: adjust xclk_max According to ov5640 datasheet xvclk is allowed to be between 6 and 54 MHz. I run a successful test with 27 MHz. Signed-off-by: Philipp Puschmann Reviewed-by: Fabio Estevam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 9e1078632375..e0a6264a6a12 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -30,7 +30,7 @@ /* min/typical/max system clock (xclk) frequencies */ #define OV5640_XCLK_MIN 6000000 -#define OV5640_XCLK_MAX 24000000 +#define OV5640_XCLK_MAX 54000000 #define OV5640_DEFAULT_SLAVE_ID 0x3c -- cgit v1.2.3-70-g09d2 From 3c4a737267e89aafa6308c6c456d2ebea3fcd085 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 20 Jun 2018 04:40:57 -0400 Subject: media: ov5640: fix frame interval enumeration Driver must reject frame interval enumeration of unsupported resolution. This was detected by v4l2-compliance format ioctl test: v4l2-compliance Format ioctls: info: found 2 frameintervals for pixel format 4745504a and size 176x144 fail: v4l2-test-formats.cpp(123): found frame intervals for invalid size 177x144 test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: FAIL Signed-off-by: Hugues Fruchet Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index e0a6264a6a12..1ecbb7a4a2ee 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1413,24 +1413,16 @@ static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) { - const struct ov5640_mode_info *mode = NULL; - int i; - - for (i = OV5640_NUM_MODES - 1; i >= 0; i--) { - mode = &ov5640_mode_data[fr][i]; - - if (!mode->reg_data) - continue; + const struct ov5640_mode_info *mode; - if ((nearest && mode->hact <= width && - mode->vact <= height) || - (!nearest && mode->hact == width && - mode->vact == height)) - break; - } + mode = v4l2_find_nearest_size(ov5640_mode_data[fr], + ARRAY_SIZE(ov5640_mode_data[fr]), + hact, vact, + width, height); - if (nearest && i < 0) - mode = &ov5640_mode_data[fr][0]; + if (!mode || + (!nearest && (mode->hact != width || mode->vact != height))) + return NULL; return mode; } @@ -2509,8 +2501,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; - sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact, - mode->vact, true); + mode = ov5640_find_mode(sensor, frame_rate, mode->hact, + mode->vact, true); + if (!mode) { + ret = -EINVAL; + goto out; + } + + sensor->current_mode = mode; sensor->pending_mode_change = true; out: mutex_unlock(&sensor->lock); -- cgit v1.2.3-70-g09d2 From 90ee26fb2f50e1e57f9d957a1799af1bcafd9671 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Tue, 19 Jun 2018 02:01:47 -0400 Subject: media: ak7375: Add ak7375 lens voice coil driver Add a v4l2 sub-device driver for the ak7375 lens voice coil. This is a voice coil module using the i2c bus to control the focus position. ak7375 can write multiple bytes of data at a time. If more data is received instead of the stop condition after receiving one byte of data, the address inside the chip is automatically incremented and the data is written into the next address. The ak7375 can control the position with 12 bits value and consists of two 8 bit registers show as below: register 0x00(AK7375_REG_POSITION): +---+---+---+---+---+---+---+---+ |D11|D10|D09|D08|D07|D06|D05|D04| +---+---+---+---+---+---+---+---+ register 0x01: +---+---+---+---+---+---+---+---+ |D03|D02|D01|D00|---|---|---|---| +---+---+---+---+---+---+---+---+ This driver support : - set ak7375 to standby mode once suspend and turn it back to active if resume - set the position via V4L2_CID_FOCUS_ABSOLUTE ctrl [Sakari Ailus: Rename val as ret in probe, drop redundant error message] Signed-off-by: Tianshu Qiu Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 10 ++ drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ak7375.c | 292 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 drivers/media/i2c/ak7375.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1cf91bce2613..8669853e1361 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -326,6 +326,16 @@ config VIDEO_AD5820 This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). +config VIDEO_AK7375 + tristate "AK7375 lens voice coil support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + help + This is a driver for the AK7375 camera lens voice coil. + AK7375 is a 12 bit DAC with 120mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_DW9714 tristate "DW9714 lens voice coil support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 1d994658a9d4..837c428339df 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o obj-$(CONFIG_VIDEO_DW9807) += dw9807.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c new file mode 100644 index 000000000000..7b14b11605ca --- /dev/null +++ b/drivers/media/i2c/ak7375.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include + +#define AK7375_MAX_FOCUS_POS 4095 +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ +#define AK7375_FOCUS_STEPS 1 +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define AK7375_CTRL_STEPS 64 +#define AK7375_CTRL_DELAY_US 1000 + +#define AK7375_REG_POSITION 0x0 +#define AK7375_REG_CONT 0x2 +#define AK7375_MODE_ACTIVE 0x0 +#define AK7375_MODE_STANDBY 0x40 + +/* ak7375 device structure */ +struct ak7375_device { + struct v4l2_ctrl_handler ctrls_vcm; + struct v4l2_subdev sd; + struct v4l2_ctrl *focus; + /* active or standby mode */ + bool active; +}; + +static inline struct ak7375_device *to_ak7375_vcm(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct ak7375_device, ctrls_vcm); +} + +static inline struct ak7375_device *sd_to_ak7375_vcm(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ak7375_device, sd); +} + +static int ak7375_i2c_write(struct ak7375_device *ak7375, + u8 addr, u16 data, u8 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ak7375->sd); + u8 buf[3]; + int ret; + + if (size != 1 && size != 2) + return -EINVAL; + buf[0] = addr; + buf[size] = data & 0xff; + if (size == 2) + buf[1] = (data >> 8) & 0xff; + ret = i2c_master_send(client, (const char *)buf, size + 1); + if (ret < 0) + return ret; + if (ret != size + 1) + return -EIO; + + return 0; +} + +static int ak7375_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ak7375_device *dev_vcm = to_ak7375_vcm(ctrl); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return ak7375_i2c_write(dev_vcm, AK7375_REG_POSITION, + ctrl->val << 4, 2); + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ak7375_vcm_ctrl_ops = { + .s_ctrl = ak7375_set_ctrl, +}; + +static int ak7375_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int ret; + + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + + return 0; +} + +static int ak7375_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops ak7375_int_ops = { + .open = ak7375_open, + .close = ak7375_close, +}; + +static const struct v4l2_subdev_ops ak7375_ops = { }; + +static void ak7375_subdev_cleanup(struct ak7375_device *ak7375_dev) +{ + v4l2_async_unregister_subdev(&ak7375_dev->sd); + v4l2_ctrl_handler_free(&ak7375_dev->ctrls_vcm); + media_entity_cleanup(&ak7375_dev->sd.entity); +} + +static int ak7375_init_controls(struct ak7375_device *dev_vcm) +{ + struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; + const struct v4l2_ctrl_ops *ops = &ak7375_vcm_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + dev_vcm->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, + 0, AK7375_MAX_FOCUS_POS, AK7375_FOCUS_STEPS, 0); + + if (hdl->error) + dev_err(dev_vcm->sd.dev, "%s fail error: 0x%x\n", + __func__, hdl->error); + dev_vcm->sd.ctrl_handler = hdl; + + return hdl->error; +} + +static int ak7375_probe(struct i2c_client *client) +{ + struct ak7375_device *ak7375_dev; + int ret; + + ak7375_dev = devm_kzalloc(&client->dev, sizeof(*ak7375_dev), + GFP_KERNEL); + if (!ak7375_dev) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak7375_dev->sd, client, &ak7375_ops); + ak7375_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ak7375_dev->sd.internal_ops = &ak7375_int_ops; + ak7375_dev->sd.entity.function = MEDIA_ENT_F_LENS; + + ret = ak7375_init_controls(ak7375_dev); + if (ret) + goto err_cleanup; + + ret = media_entity_pads_init(&ak7375_dev->sd.entity, 0, NULL); + if (ret < 0) + goto err_cleanup; + + ret = v4l2_async_register_subdev(&ak7375_dev->sd); + if (ret < 0) + goto err_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +err_cleanup: + v4l2_ctrl_handler_free(&ak7375_dev->ctrls_vcm); + media_entity_cleanup(&ak7375_dev->sd.entity); + + return ret; +} + +static int ak7375_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + + ak7375_subdev_cleanup(ak7375_dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +/* + * This function sets the vcm position, so it consumes least current + * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused ak7375_vcm_suspend(struct device *dev) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + int ret, val; + + if (!ak7375_dev->active) + return 0; + + for (val = ak7375_dev->focus->val & ~(AK7375_CTRL_STEPS - 1); + val >= 0; val -= AK7375_CTRL_STEPS) { + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, + val << 4, 2); + if (ret) + dev_err_once(dev, "%s I2C failure: %d\n", + __func__, ret); + usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + } + + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, + AK7375_MODE_STANDBY, 1); + if (ret) + dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + + ak7375_dev->active = false; + + return 0; +} + +/* + * This function sets the vcm position to the value set by the user + * through v4l2_ctrl_ops s_ctrl handler + * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused ak7375_vcm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + int ret, val; + + if (ak7375_dev->active) + return 0; + + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, + AK7375_MODE_ACTIVE, 1); + if (ret) { + dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + return ret; + } + + for (val = ak7375_dev->focus->val % AK7375_CTRL_STEPS; + val <= ak7375_dev->focus->val; + val += AK7375_CTRL_STEPS) { + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, + val << 4, 2); + if (ret) + dev_err_ratelimited(dev, "%s I2C failure: %d\n", + __func__, ret); + usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + } + + ak7375_dev->active = true; + + return 0; +} + +static const struct of_device_id ak7375_of_table[] = { + { .compatible = "asahi-kasei,ak7375" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ak7375_of_table); + +static const struct dev_pm_ops ak7375_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ak7375_vcm_suspend, ak7375_vcm_resume) + SET_RUNTIME_PM_OPS(ak7375_vcm_suspend, ak7375_vcm_resume, NULL) +}; + +static struct i2c_driver ak7375_i2c_driver = { + .driver = { + .name = "ak7375", + .pm = &ak7375_pm_ops, + .of_match_table = ak7375_of_table, + }, + .probe_new = ak7375_probe, + .remove = ak7375_remove, +}; +module_i2c_driver(ak7375_i2c_driver); + +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Bingbu Cao "); +MODULE_DESCRIPTION("AK7375 VCM driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 438ac1fd5e1e132782795ba474b97fffbc79bbd5 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:32 -0400 Subject: media: imx274: initialize format before v4l2 controls The current probe function calls v4l2_ctrl_handler_setup() before initializing the format info. This triggers call paths such as: imx274_probe -> v4l2_ctrl_handler_setup -> imx274_s_ctrl -> imx274_set_exposure, where priv->mode_index is accessed before being assigned. This is wrong but does not trigger a visible bug because priv is zero-initialized and 0 is the default value for priv->mode_index. But this would become a crash in follow-up commits when mode_index is replaced by a pointer that must always be valid. Fix the bug before it shows up by initializing struct members early. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 63fb94e7da37..8a8a11b8d75d 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1632,6 +1632,16 @@ static int imx274_probe(struct i2c_client *client, mutex_init(&imx274->lock); + /* initialize format */ + imx274->mode_index = IMX274_MODE_3840X2160; + imx274->format.width = imx274_formats[0].size.width; + imx274->format.height = imx274_formats[0].size.height; + imx274->format.field = V4L2_FIELD_NONE; + imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + imx274->format.colorspace = V4L2_COLORSPACE_SRGB; + imx274->frame_interval.numerator = 1; + imx274->frame_interval.denominator = IMX274_DEF_FRAME_RATE; + /* initialize regmap */ imx274->regmap = devm_regmap_init_i2c(client, &imx274_regmap_config); if (IS_ERR(imx274->regmap)) { @@ -1720,16 +1730,6 @@ static int imx274_probe(struct i2c_client *client, goto err_ctrls; } - /* initialize format */ - imx274->mode_index = IMX274_MODE_3840X2160; - imx274->format.width = imx274_formats[0].size.width; - imx274->format.height = imx274_formats[0].size.height; - imx274->format.field = V4L2_FIELD_NONE; - imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; - imx274->format.colorspace = V4L2_COLORSPACE_SRGB; - imx274->frame_interval.numerator = 1; - imx274->frame_interval.denominator = IMX274_DEF_FRAME_RATE; - /* load default control values */ ret = imx274_load_default(imx274); if (ret) { -- cgit v1.2.3-70-g09d2 From 96a2c731a4e661470d3a0a39d5efc792a22c7bba Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:33 -0400 Subject: media: imx274: consolidate per-mode data in imx274_frmfmt Data about the implemented readout modes is partially stored in imx274_formats[], the rest is scattered in several arrays. The latter are then accessed using the mode index, e.g.: min_frame_len[priv->mode_index] Consolidate all these data in imx274_formats[], and store a pointer to the selected mode (i.e. imx274_formats[priv->mode_index]) in the main device struct. This way code to use these data becomes more readable, e.g.: priv->mode->min_frame_len This removes lots of scaffolding code and keeps data about each mode in a unique place. Also remove a parameter to imx274_mode_regs() that is now unused. While this adds the mode pointer to the device struct, it does not remove the mode_index from it because mode_index is still used in two dev_dbg() calls. This will be handled in a follow-up commit. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 139 +++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 73 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 8a8a11b8d75d..2ec31ae4e60d 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -147,10 +147,28 @@ enum imx274_mode { }; /* - * imx274 format related structure + * Parameters for each imx274 readout mode. + * + * These are the values to configure the sensor in one of the + * implemented modes. + * + * @size: recommended recording pixels + * @init_regs: registers to initialize the mode + * @min_frame_len: Minimum frame length for each mode (see "Frame Rate + * Adjustment (CSI-2)" in the datasheet) + * @min_SHR: Minimum SHR register value (see "Shutter Setting (CSI-2)" in the + * datasheet) + * @max_fps: Maximum frames per second + * @nocpiop: Number of clocks per internal offset period (see "Integration Time + * in Each Readout Drive Mode (CSI-2)" in the datasheet) */ struct imx274_frmfmt { struct v4l2_frmsize_discrete size; + const struct reg_8 *init_regs; + int min_frame_len; + int min_SHR; + int max_fps; + int nocpiop; }; /* @@ -476,58 +494,35 @@ static const struct reg_8 imx274_tp_regs[] = { {IMX274_TABLE_END, 0x00} }; -static const struct reg_8 *mode_table[] = { - [IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10, - [IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10, - [IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10, -}; - -/* - * imx274 format related structure - */ +/* nocpiop happens to be the same number for the implemented modes */ static const struct imx274_frmfmt imx274_formats[] = { - { {3840, 2160} }, - { {1920, 1080} }, - { {1280, 720} }, -}; - -/* - * minimal frame length for each mode - * refer to datasheet section "Frame Rate Adjustment (CSI-2)" - */ -static const int min_frame_len[] = { - 4550, /* mode 1, 4K */ - 2310, /* mode 3, 1080p */ - 2310 /* mode 5, 720p */ -}; - -/* - * minimal numbers of SHR register - * refer to datasheet table "Shutter Setting (CSI-2)" - */ -static const int min_SHR[] = { - 12, /* mode 1, 4K */ - 8, /* mode 3, 1080p */ - 8 /* mode 5, 720p */ -}; - -static const int max_frame_rate[] = { - 60, /* mode 1 , 4K */ - 120, /* mode 3, 1080p */ - 120 /* mode 5, 720p */ -}; - -/* - * Number of clocks per internal offset period - * a constant based on mode - * refer to section "Integration Time in Each Readout Drive Mode (CSI-2)" - * in the datasheet - * for the implemented 3 modes, it happens to be the same number - */ -static const int nocpiop[] = { - 112, /* mode 1 , 4K */ - 112, /* mode 3, 1080p */ - 112 /* mode 5, 720p */ + { + /* mode 1, 4K */ + .size = {3840, 2160}, + .init_regs = imx274_mode1_3840x2160_raw10, + .min_frame_len = 4550, + .min_SHR = 12, + .max_fps = 60, + .nocpiop = 112, + }, + { + /* mode 3, 1080p */ + .size = {1920, 1080}, + .init_regs = imx274_mode3_1920x1080_raw10, + .min_frame_len = 2310, + .min_SHR = 8, + .max_fps = 120, + .nocpiop = 112, + }, + { + /* mode 5, 720p */ + .size = {1280, 720}, + .init_regs = imx274_mode5_1280x720_raw10, + .min_frame_len = 2310, + .min_SHR = 8, + .max_fps = 120, + .nocpiop = 112, + }, }; /* @@ -557,6 +552,8 @@ struct imx274_ctrls { * @regmap: Pointer to regmap structure * @reset_gpio: Pointer to reset gpio * @lock: Mutex structure + * @mode: Parameters for the selected readout mode + * (points to imx274_formats[mode_index]) * @mode_index: Resolution mode index */ struct stimx274 { @@ -569,6 +566,7 @@ struct stimx274 { struct regmap *regmap; struct gpio_desc *reset_gpio; struct mutex lock; /* mutex lock for operations */ + const struct imx274_frmfmt *mode; u32 mode_index; }; @@ -704,18 +702,12 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) } /* - * imx274_mode_regs - Function for set mode registers per mode index + * Set mode registers to start stream. * @priv: Pointer to device structure - * @mode: Mode index value - * - * This is used to start steam per mode index. - * mode = 0, start stream for sensor Mode 1: 4K/raw10 - * mode = 1, start stream for sensor Mode 3: 1080p/raw10 - * mode = 2, start stream for sensor Mode 5: 720p/raw10 * * Return: 0 on success, errors otherwise */ -static int imx274_mode_regs(struct stimx274 *priv, int mode) +static int imx274_mode_regs(struct stimx274 *priv) { int err = 0; @@ -727,7 +719,7 @@ static int imx274_mode_regs(struct stimx274 *priv, int mode) if (err) return err; - err = imx274_write_table(priv, mode_table[mode]); + err = imx274_write_table(priv, priv->mode->init_regs); return err; } @@ -889,6 +881,7 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, } imx274->mode_index = index; + imx274->mode = &imx274_formats[index]; if (fmt->width > IMX274_MAX_WIDTH) fmt->width = IMX274_MAX_WIDTH; @@ -1042,7 +1035,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) if (on) { /* load mode registers */ - ret = imx274_mode_regs(imx274, imx274->mode_index); + ret = imx274_mode_regs(imx274); if (ret) goto fail; @@ -1146,14 +1139,14 @@ static int imx274_clamp_coarse_time(struct stimx274 *priv, u32 *val, if (err) return err; - if (*frame_length < min_frame_len[priv->mode_index]) - *frame_length = min_frame_len[priv->mode_index]; + if (*frame_length < priv->mode->min_frame_len) + *frame_length = priv->mode->min_frame_len; *val = *frame_length - *val; /* convert to raw shr */ if (*val > *frame_length - IMX274_SHR_LIMIT_CONST) *val = *frame_length - IMX274_SHR_LIMIT_CONST; - else if (*val < min_SHR[priv->mode_index]) - *val = min_SHR[priv->mode_index]; + else if (*val < priv->mode->min_SHR) + *val = priv->mode->min_SHR; return 0; } @@ -1365,7 +1358,7 @@ static int imx274_set_exposure(struct stimx274 *priv, int val) } coarse_time = (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2 * val - - nocpiop[priv->mode_index]) / hmax; + - priv->mode->nocpiop) / hmax; /* step 2: convert exposure_time into SHR value */ @@ -1375,7 +1368,7 @@ static int imx274_set_exposure(struct stimx274 *priv, int val) goto fail; priv->ctrls.exposure->val = - (coarse_time * hmax + nocpiop[priv->mode_index]) + (coarse_time * hmax + priv->mode->nocpiop) / (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2); dev_dbg(&priv->client->dev, @@ -1529,10 +1522,9 @@ static int imx274_set_frame_interval(struct stimx274 *priv, / frame_interval.numerator); /* boundary check */ - if (req_frame_rate > max_frame_rate[priv->mode_index]) { + if (req_frame_rate > priv->mode->max_fps) { frame_interval.numerator = 1; - frame_interval.denominator = - max_frame_rate[priv->mode_index]; + frame_interval.denominator = priv->mode->max_fps; } else if (req_frame_rate < IMX274_MIN_FRAME_RATE) { frame_interval.numerator = 1; frame_interval.denominator = IMX274_MIN_FRAME_RATE; @@ -1634,8 +1626,9 @@ static int imx274_probe(struct i2c_client *client, /* initialize format */ imx274->mode_index = IMX274_MODE_3840X2160; - imx274->format.width = imx274_formats[0].size.width; - imx274->format.height = imx274_formats[0].size.height; + imx274->mode = &imx274_formats[imx274->mode_index]; + imx274->format.width = imx274->mode->size.width; + imx274->format.height = imx274->mode->size.height; imx274->format.field = V4L2_FIELD_NONE; imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; imx274->format.colorspace = V4L2_COLORSPACE_SRGB; -- cgit v1.2.3-70-g09d2 From 4317322d5a10467e75cfb5b12e13e5e6f3261b25 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:34 -0400 Subject: media: imx274: get rid of mode_index After restructuring struct imx274_frmfmt, the mode_index field is still in use only for two dev_dbg() calls in imx274_s_stream(). Let's remove it and avoid duplicated information. Replacing the first usage requires some rather annoying but trivial pointer math. The other one can be removed entirely since it would print the same value anyway. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 2ec31ae4e60d..f075715ffced 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -553,8 +553,6 @@ struct imx274_ctrls { * @reset_gpio: Pointer to reset gpio * @lock: Mutex structure * @mode: Parameters for the selected readout mode - * (points to imx274_formats[mode_index]) - * @mode_index: Resolution mode index */ struct stimx274 { struct v4l2_subdev sd; @@ -567,7 +565,6 @@ struct stimx274 { struct gpio_desc *reset_gpio; struct mutex lock; /* mutex lock for operations */ const struct imx274_frmfmt *mode; - u32 mode_index; }; /* @@ -880,7 +877,6 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, index = 0; } - imx274->mode_index = index; imx274->mode = &imx274_formats[index]; if (fmt->width > IMX274_MAX_WIDTH) @@ -1028,8 +1024,9 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) struct stimx274 *imx274 = to_imx274(sd); int ret = 0; - dev_dbg(&imx274->client->dev, "%s : %s, mode index = %d\n", __func__, - on ? "Stream Start" : "Stream Stop", imx274->mode_index); + dev_dbg(&imx274->client->dev, "%s : %s, mode index = %td\n", __func__, + on ? "Stream Start" : "Stream Stop", + imx274->mode - &imx274_formats[0]); mutex_lock(&imx274->lock); @@ -1068,8 +1065,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) } mutex_unlock(&imx274->lock); - dev_dbg(&imx274->client->dev, - "%s : Done: mode = %d\n", __func__, imx274->mode_index); + dev_dbg(&imx274->client->dev, "%s : Done\n", __func__); return 0; fail: @@ -1625,8 +1621,7 @@ static int imx274_probe(struct i2c_client *client, mutex_init(&imx274->lock); /* initialize format */ - imx274->mode_index = IMX274_MODE_3840X2160; - imx274->mode = &imx274_formats[imx274->mode_index]; + imx274->mode = &imx274_formats[IMX274_MODE_3840X2160]; imx274->format.width = imx274->mode->size.width; imx274->format.height = imx274->mode->size.height; imx274->format.field = V4L2_FIELD_NONE; -- cgit v1.2.3-70-g09d2 From 0b4c9553b135a511f3c5c565ea2b48e801a5f81f Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:35 -0400 Subject: media: imx274: actually use IMX274_DEFAULT_MODE IMX274_DEFAULT_MODE is defined but not used. Start using it, so the default can be more easily changed without digging into the code. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f075715ffced..ceeec97cd330 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1621,7 +1621,7 @@ static int imx274_probe(struct i2c_client *client, mutex_init(&imx274->lock); /* initialize format */ - imx274->mode = &imx274_formats[IMX274_MODE_3840X2160]; + imx274->mode = &imx274_formats[IMX274_DEFAULT_MODE]; imx274->format.width = imx274->mode->size.width; imx274->format.height = imx274->mode->size.height; imx274->format.field = V4L2_FIELD_NONE; -- cgit v1.2.3-70-g09d2 From 7ff67863c690fbe1e10c70cefbb718b36c155a32 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:36 -0400 Subject: media: imx274: simplify imx274_write_table() imx274_write_table() is a mere wrapper (and the only user) to imx274_regmap_util_write_table_8(). Remove this useless indirection by merging the two functions into one. Also get rid of the wait_ms_addr and end_addr parameters since it does not make any sense to give them any values other than IMX274_TABLE_WAIT_MS and IMX274_TABLE_END. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index ceeec97cd330..48343c2ade83 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -597,20 +597,18 @@ static inline struct stimx274 *to_imx274(struct v4l2_subdev *sd) } /* - * imx274_regmap_util_write_table_8 - Function for writing register table - * @regmap: Pointer to device reg map structure - * @table: Table containing register values - * @wait_ms_addr: Flag for performing delay - * @end_addr: Flag for incating end of table + * Writing a register table + * + * @priv: Pointer to device + * @table: Table containing register values (with optional delays) * * This is used to write register table into sensor's reg map. * * Return: 0 on success, errors otherwise */ -static int imx274_regmap_util_write_table_8(struct regmap *regmap, - const struct reg_8 table[], - u16 wait_ms_addr, u16 end_addr) +static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) { + struct regmap *regmap = priv->regmap; int err = 0; const struct reg_8 *next; u8 val; @@ -622,8 +620,8 @@ static int imx274_regmap_util_write_table_8(struct regmap *regmap, for (next = table;; next++) { if ((next->addr != range_start + range_count) || - (next->addr == end_addr) || - (next->addr == wait_ms_addr) || + (next->addr == IMX274_TABLE_END) || + (next->addr == IMX274_TABLE_WAIT_MS) || (range_count == max_range_vals)) { if (range_count == 1) err = regmap_write(regmap, @@ -642,10 +640,10 @@ static int imx274_regmap_util_write_table_8(struct regmap *regmap, range_count = 0; /* Handle special address values */ - if (next->addr == end_addr) + if (next->addr == IMX274_TABLE_END) break; - if (next->addr == wait_ms_addr) { + if (next->addr == IMX274_TABLE_WAIT_MS) { msleep_range(next->val); continue; } @@ -692,12 +690,6 @@ static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) return err; } -static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) -{ - return imx274_regmap_util_write_table_8(priv->regmap, - table, IMX274_TABLE_WAIT_MS, IMX274_TABLE_END); -} - /* * Set mode registers to start stream. * @priv: Pointer to device structure -- cgit v1.2.3-70-g09d2 From 19d38b235b88e0d28e40fb091b52fc10050e0d5e Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 11 Jun 2018 07:35:38 -0400 Subject: media: imx274: fix typo pd -> pad Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 48343c2ade83..edca63eaea9b 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -544,7 +544,7 @@ struct imx274_ctrls { /* * struct stim274 - imx274 device structure * @sd: V4L2 subdevice structure - * @pd: Media pad structure + * @pad: Media pad structure * @client: Pointer to I2C client * @ctrls: imx274 control structure * @format: V4L2 media bus frame format structure -- cgit v1.2.3-70-g09d2 From d862bc08ffcb45316962a4d59c6d4ffb51f9dbfa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Jun 2018 15:07:21 -0400 Subject: media: v4l2-ioctl.c: use correct vb2_queue lock for m2m devices For m2m devices the vdev->queue lock was always taken instead of the lock for the specific capture or output queue. Now that we pushed the locking down into __video_do_ioctl() we can pick the correct lock and potentially improve the performance of m2m devices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 56 ++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index eeed14468a17..01670567641a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -2670,11 +2671,62 @@ static bool v4l2_is_known_ioctl(unsigned int cmd) return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } +#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) +static bool v4l2_ioctl_m2m_queue_is_output(unsigned int cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_CREATE_BUFS: { + struct v4l2_create_buffers *cbufs = arg; + + return V4L2_TYPE_IS_OUTPUT(cbufs->format.type); + } + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *rbufs = arg; + + return V4L2_TYPE_IS_OUTPUT(rbufs->type); + } + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + case VIDIOC_QUERYBUF: + case VIDIOC_PREPARE_BUF: { + struct v4l2_buffer *buf = arg; + + return V4L2_TYPE_IS_OUTPUT(buf->type); + } + case VIDIOC_EXPBUF: { + struct v4l2_exportbuffer *expbuf = arg; + + return V4L2_TYPE_IS_OUTPUT(expbuf->type); + } + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: { + int *type = arg; + + return V4L2_TYPE_IS_OUTPUT(*type); + } + default: + return false; + } +} +#endif + static struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, - unsigned int cmd) + struct v4l2_fh *vfh, unsigned int cmd, + void *arg) { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return vdev->lock; +#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) + if (vfh && vfh->m2m_ctx && + (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) { + bool is_output = v4l2_ioctl_m2m_queue_is_output(cmd, arg); + struct v4l2_m2m_queue_ctx *ctx = is_output ? + &vfh->m2m_ctx->out_q_ctx : &vfh->m2m_ctx->cap_q_ctx; + + if (ctx->q.lock) + return ctx->q.lock; + } +#endif if (vdev->queue && vdev->queue->lock && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) return vdev->queue->lock; @@ -2741,7 +2793,7 @@ static long __video_do_ioctl(struct file *file, if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; - lock = v4l2_ioctl_get_lock(vfd, cmd); + lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg); if (lock && mutex_lock_interruptible(lock)) return -ERESTARTSYS; -- cgit v1.2.3-70-g09d2 From cd63c0288fd760ce7de247fba618e2bbcfc0c35c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:22 -0400 Subject: media: sta2x11: Add video_device and vb2_queue locks Currently, this driver does not serialize its video4linux ioctls, which is a bug, as race conditions might appear. In addition, video_device and vb2_queue locks are now both mandatory. Add them, and implement wait_prepare and wait_finish. To stay on the safe side, this commit uses a single mutex for both locks. Better latency can be obtained by separating these if needed. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 069c4a853346..da2b2a2292d0 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -145,6 +145,7 @@ struct sta2x11_vip { unsigned int sequence; struct vip_buffer *active; /* current active buffer */ spinlock_t lock; /* Used in videobuf2 callback */ + struct mutex v4l_lock; /* Interrupt counters */ int tcount, bcount; @@ -385,6 +386,8 @@ static const struct vb2_ops vip_video_qops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; @@ -870,6 +873,7 @@ static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vip->vb_vidq.dev = &vip->pdev->dev; + vip->vb_vidq.lock = &vip->v4l_lock; err = vb2_queue_init(&vip->vb_vidq); if (err) return err; @@ -1034,6 +1038,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->std = V4L2_STD_PAL; vip->format = formats_50[0]; vip->config = config; + mutex_init(&vip->v4l_lock); ret = sta2x11_vip_init_controls(vip); if (ret) @@ -1080,6 +1085,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->video_dev = video_dev_template; vip->video_dev.v4l2_dev = &vip->v4l2_dev; vip->video_dev.queue = &vip->vb_vidq; + vip->video_dev.lock = &vip->v4l_lock; video_set_drvdata(&vip->video_dev, vip); ret = video_register_device(&vip->video_dev, VFL_TYPE_GRABBER, -1); -- cgit v1.2.3-70-g09d2 From f1db5d978d88c75cfb1bcac74fbf06f77b86b7c3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:25 -0400 Subject: media: mtk-mdp: Add locks for capture and output vb2_queues Use the mutex in struct mtk_mdp_ctx to protect the capture and output vb2_queues. This allows to replace the ad-hoc wait_{prepare, finish} with vb2_ops_wait_{prepare, finish}. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 583d47724ee8..c2e2f5f1ebf1 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -394,20 +394,6 @@ static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) return ret; } -static void mtk_mdp_ctx_lock(struct vb2_queue *vq) -{ - struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_lock(&ctx->mdp_dev->lock); -} - -static void mtk_mdp_ctx_unlock(struct vb2_queue *vq) -{ - struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_unlock(&ctx->mdp_dev->lock); -} - static void mtk_mdp_set_frame_size(struct mtk_mdp_frame *frame, int width, int height) { @@ -625,10 +611,10 @@ static const struct vb2_ops mtk_mdp_m2m_qops = { .queue_setup = mtk_mdp_m2m_queue_setup, .buf_prepare = mtk_mdp_m2m_buf_prepare, .buf_queue = mtk_mdp_m2m_buf_queue, - .wait_prepare = mtk_mdp_ctx_unlock, - .wait_finish = mtk_mdp_ctx_lock, .stop_streaming = mtk_mdp_m2m_stop_streaming, .start_streaming = mtk_mdp_m2m_start_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int mtk_mdp_m2m_querycap(struct file *file, void *fh, @@ -996,6 +982,7 @@ static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = &ctx->mdp_dev->pdev->dev; + src_vq->lock = &ctx->mdp_dev->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -1010,6 +997,7 @@ static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = &ctx->mdp_dev->pdev->dev; + dst_vq->lock = &ctx->mdp_dev->lock; return vb2_queue_init(dst_vq); } -- cgit v1.2.3-70-g09d2 From b53e19e16153ff8a0a560151ed27c3bc8d846de2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:26 -0400 Subject: media: s5p-g2d: Implement wait_prepare and wait_finish This driver is currently specifying a vb2_queue lock, which means it straightforward to implement wait_prepare and wait_finish. Having these callbacks releases the queue lock while blocking, which improves latency by allowing for example streamoff or qbuf operations while waiting in dqbuf. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 3735c204e9ac..6b1c23463009 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -142,6 +142,8 @@ static const struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, .buf_queue = g2d_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, -- cgit v1.2.3-70-g09d2 From a4273abcd3e40f7e8fe9c5347dea1e0a6b9ccadb Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:30 -0400 Subject: media: mx_emmaprp: Implement wait_prepare and wait_finish This driver is currently specifying a video_device lock, which means it is protecting all the ioctls (including queue ioctls) with a single mutex. It's therefore straightforward to implement wait_prepare and wait_finish, by explicitly setting the vb2_queue lock. Having these callbacks releases the queue lock while blocking, which improves latency by allowing for example streamoff or qbuf operations while waiting in dqbuf. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mx2_emmaprp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 83b9db895a64..64195c4ddeaf 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -733,6 +733,8 @@ static const struct vb2_ops emmaprp_qops = { .queue_setup = emmaprp_queue_setup, .buf_prepare = emmaprp_buf_prepare, .buf_queue = emmaprp_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -749,6 +751,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = ctx->dev->v4l2_dev.dev; + src_vq->lock = &ctx->dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -762,6 +765,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = ctx->dev->v4l2_dev.dev; + dst_vq->lock = &ctx->dev->dev_mutex; return vb2_queue_init(dst_vq); } -- cgit v1.2.3-70-g09d2 From 1d120649a81e5fc71151cbe8d6d82c58f7777160 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:31 -0400 Subject: media: m2m-deinterlace: Implement wait_prepare and wait_finish This driver is currently specifying a video_device lock, which means it is protecting all the ioctls (including queue ioctls) with a single mutex. It's therefore straightforward to implement wait_prepare and wait_finish, by explicitly setting the vb2_queue lock. Having these callbacks releases the queue lock while blocking, which improves latency by allowing for example streamoff or qbuf operations while waiting in dqbuf. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index a566ec566cec..5f84d2aa47ab 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -842,6 +842,8 @@ static const struct vb2_ops deinterlace_qops = { .queue_setup = deinterlace_queue_setup, .buf_prepare = deinterlace_buf_prepare, .buf_queue = deinterlace_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -858,6 +860,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = ctx->dev->v4l2_dev.dev; + src_vq->lock = &ctx->dev->dev_mutex; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -876,6 +879,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = ctx->dev->v4l2_dev.dev; + dst_vq->lock = &ctx->dev->dev_mutex; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; -- cgit v1.2.3-70-g09d2 From de3e581f31626a9e5697ea1581967729fcffdd1d Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jun 2018 15:07:32 -0400 Subject: media: stk1160: Set the vb2_queue lock before calling vb2_queue_init The vb2_queue will soon be mandatory. The videobuf2 core will throw a verbose warning if it's not set. The stk1160 driver is setting the queue lock, but after the vb2_queue_init call. Avoid the warning by setting the lock before the queue initialization. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 77b759a0bcd9..504e413edcd2 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -802,6 +802,7 @@ int stk1160_vb2_setup(struct stk1160 *dev) q->buf_struct_size = sizeof(struct stk1160_buffer); q->ops = &stk1160_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->lock = &dev->vb_queue_lock; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(q); @@ -827,7 +828,6 @@ int stk1160_video_register(struct stk1160 *dev) * It will be used to protect *only* v4l2 ioctls. */ dev->vdev.lock = &dev->v4l_lock; - dev->vdev.queue->lock = &dev->vb_queue_lock; /* This will be used to set video_device parent */ dev->vdev.v4l2_dev = &dev->v4l2_dev; -- cgit v1.2.3-70-g09d2 From 18c227af2c90eee06bd90f751a4b1193ee209d56 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 11:44:12 -0400 Subject: media: coda: fix encoder source stride The encoder picture run command takes a picture source stride parameter. This must be set to the output queue's bytesperline, not width. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 68ed2a564ad1..a585b80ca3ac 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1362,7 +1362,8 @@ static int coda_prepare_encode(struct coda_ctx *ctx) if (dev->devtype->product == CODA_960) { coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); - coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, q_data_src->bytesperline, + CODA9_CMD_ENC_PIC_SRC_STRIDE); coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; -- cgit v1.2.3-70-g09d2 From 42a68012e67c2613e0570d462c5a0bd9a1527048 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 11:47:08 -0400 Subject: media: coda: add read-only h.264 decoder profile/level controls The decoder profile/level controls initially can be used to determine supported profiles and levels. The values are set for a given stream once the headers are parsed. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 115 +++++++++++++++++++++++++++++- drivers/media/platform/coda/coda.h | 2 + 2 files changed, 116 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index b86d704ae10c..f41f2035204d 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1395,6 +1395,72 @@ static int coda_buf_prepare(struct vb2_buffer *vb) return 0; } +static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) +{ + if (!ctrl) + return; + + v4l2_ctrl_lock(ctrl); + + /* + * Extend the control range if the parsed stream contains a known but + * unsupported value or level. + */ + if (value > ctrl->maximum) { + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } else if (value < ctrl->minimum) { + __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } + + __v4l2_ctrl_s_ctrl(ctrl, value); + + v4l2_ctrl_unlock(ctrl); +} + +static void coda_update_h264_profile_ctrl(struct coda_ctx *ctx) +{ + const char * const *profile_names; + int profile; + + profile = coda_h264_profile(ctx->params.h264_profile_idc); + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Profile: %u\n", + ctx->params.h264_profile_idc); + return; + } + + coda_update_menu_ctrl(ctx->h264_profile_ctrl, profile); + + profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Parsed H264 Profile: %s\n", + profile_names[profile]); +} + +static void coda_update_h264_level_ctrl(struct coda_ctx *ctx) +{ + const char * const *level_names; + int level; + + level = coda_h264_level(ctx->params.h264_level_idc); + if (level < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Level: %u\n", + ctx->params.h264_level_idc); + return; + } + + coda_update_menu_ctrl(ctx->h264_level_ctrl, level); + + level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Parsed H264 Level: %s\n", + level_names[level]); +} + static void coda_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1423,8 +1489,11 @@ static void coda_buf_queue(struct vb2_buffer *vb) * whether to enable reordering during sequence * initialization. */ - if (!ctx->params.h264_profile_idc) + if (!ctx->params.h264_profile_idc) { coda_sps_parse_profile(ctx, vb); + coda_update_h264_profile_ctrl(ctx); + coda_update_h264_level_ctrl(ctx); + } } mutex_lock(&ctx->bitstream_mutex); @@ -1862,6 +1931,47 @@ static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); } +static void coda_decode_ctrls(struct coda_ctx *ctx) +{ + u64 mask; + u8 max; + + ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + if (ctx->h264_profile_ctrl) + ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) { + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + mask = ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0)); + } else if (ctx->dev->devtype->product == CODA_960) { + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + mask = ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1)); + } else { + return; + } + ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, mask, + max); + if (ctx->h264_level_ctrl) + ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; +} + static int coda_ctrls_setup(struct coda_ctx *ctx) { v4l2_ctrl_handler_init(&ctx->ctrls, 2); @@ -1875,6 +1985,9 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) coda_jpeg_encode_ctrls(ctx); else coda_encode_ctrls(ctx); + } else { + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) + coda_decode_ctrls(ctx); } if (ctx->ctrls.error) { diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index c70cfab2433f..9f57f73161d6 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -214,6 +214,8 @@ struct coda_ctx { enum v4l2_quantization quantization; struct coda_params params; struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *h264_profile_ctrl; + struct v4l2_ctrl *h264_level_ctrl; struct v4l2_fh fh; int gopcounter; int runcounter; -- cgit v1.2.3-70-g09d2 From 9f84310d88304bb2f2b154ba740ecc3a6326c175 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 11:47:09 -0400 Subject: media: coda: fix reorder detection for unknown levels Whether reordering should be enabled only depends on the h.264 profile. Stop parsing the level and drop the debug message, profile and level can now be determined via read-only decoder controls. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index a585b80ca3ac..36c19983910b 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1583,10 +1583,8 @@ static int coda_decoder_reqbufs(struct coda_ctx *ctx, static bool coda_reorder_enable(struct coda_ctx *ctx) { - const char * const *profile_names; - const char * const *level_names; struct coda_dev *dev = ctx->dev; - int profile, level; + int profile; if (dev->devtype->product != CODA_HX4 && dev->devtype->product != CODA_7541 && @@ -1600,24 +1598,9 @@ static bool coda_reorder_enable(struct coda_ctx *ctx) return true; profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) { - v4l2_warn(&dev->v4l2_dev, "Invalid H264 Profile: %d\n", - ctx->params.h264_profile_idc); - return false; - } - - level = coda_h264_level(ctx->params.h264_level_idc); - if (level < 0) { - v4l2_warn(&dev->v4l2_dev, "Invalid H264 Level: %d\n", - ctx->params.h264_level_idc); - return false; - } - - profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); - level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "H264 Profile/Level: %s L%s\n", - profile_names[profile], level_names[level]); + if (profile < 0) + v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", + ctx->params.h264_profile_idc); /* Baseline profile does not support reordering */ return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; -- cgit v1.2.3-70-g09d2 From 83a729530a3a8a40dd529c72e4c3fe491d9bce68 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 29 Jun 2018 05:40:41 -0400 Subject: media: vivid: fix gain when autogain is on In the vivid driver you want gain to continuous change while autogain is on. However, dev->jiffies_vid_cap doesn't actually change. It probably did in the past, but changes in the code caused this to be a fixed value that is only set when you start streaming. Replace it by jiffies, which is always changing. [mchehab@kernel.org: use jiffies_to_msecs() instead of dividing by HZ] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 6b0bfa091592..5429193fbb91 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -295,7 +295,7 @@ static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - dev->gain->val = dev->jiffies_vid_cap & 0xff; + dev->gain->val = (jiffies_to_msecs(jiffies) / 1000) & 0xff; break; } return 0; -- cgit v1.2.3-70-g09d2 From 4d2096b4695623e3683cd41a96d6d0af6a5acf87 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jun 2018 08:42:08 -0400 Subject: media: coda: clear hold flag on streamoff If new buffers are queued after streamoff, the flag will be cleared anyway, so this is mostly for the purpose of correctness. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index f41f2035204d..42f852ccc8c0 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1703,6 +1703,7 @@ static void coda_stop_streaming(struct vb2_queue *q) ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; ctx->aborting = 0; + ctx->hold = false; } if (!ctx->streamon_out && !ctx->streamon_cap) -- cgit v1.2.3-70-g09d2 From 1e3e2a9ac40ad4d11699a49a1bbbf40cd8d4c8bd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jun 2018 08:46:46 -0400 Subject: media: coda: jpeg: allow non-JPEG colorspace The hardware codec is not colorspace aware. We should trust userspace to set the correct colorimetry information on the OUTPUT queue and mirror the exact same setting on the CAPTURE queue. There is no reason to restrict colorspace to JPEG for JPEG images, if userspace injects the correct colorspace information into the JPEG headers after encoding. Fixes: b14ac545688d ("[media] coda: improve colorimetry handling") Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 42f852ccc8c0..f25f1417b4fa 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -569,8 +569,6 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, f->fmt.pix.height * 2; break; case V4L2_PIX_FMT_JPEG: - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - /* fallthrough */ case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_MPEG4: case V4L2_PIX_FMT_MPEG2: -- cgit v1.2.3-70-g09d2 From 86b30a671b3bbe376ad7679ae3c1436c2a8f4510 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jun 2018 08:46:47 -0400 Subject: media: coda: jpeg: only queue two buffers into the bitstream for JPEG on CODA7541 Padding the bitstream buffer is not enough to reliably avoid prefetch failures. Picture runs with the next buffer's header already visible to the CODA7541 succeed much more reliably, so always queue two JPEG frames into the bitstream buffer. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 36c19983910b..e2994b3ddbf2 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -261,11 +261,12 @@ void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { /* - * Only queue a single JPEG into the bitstream buffer, except - * to increase payload over 512 bytes or if in hold state. + * Only queue two JPEGs into the bitstream buffer to keep + * latency low. We need at least one complete buffer and the + * header of another buffer (for prescan) in the bitstream. */ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - (coda_get_bitstream_payload(ctx) >= 512) && !ctx->hold) + ctx->num_metas > 1) break; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); -- cgit v1.2.3-70-g09d2 From 2b84e2a0092c74e4ccc7933a06014e850c905a55 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jun 2018 08:46:48 -0400 Subject: media: coda: jpeg: explicitly disable thumbnails in SEQ_INIT Explicitly clear DEC_SEQ_JPG_THUMB_EN during sequence initialization. Not clearing the register does not cause problems, since the only other codec (MPEG-4 decode) that writes to this register happens to always write 0 as well. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 2 ++ drivers/media/platform/coda/coda_regs.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index e2994b3ddbf2..92044b779f59 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1679,6 +1679,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); } } + if (src_fourcc == V4L2_PIX_FMT_JPEG) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); if (dev->devtype->product != CODA_960) coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index 3b650b8aabe9..5e7b00a97671 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -157,6 +157,7 @@ #define CODA_CMD_DEC_SEQ_START_BYTE 0x190 #define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 #define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 +#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c #define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c #define CODA_MP4_CLASS_MPEG4 0 #define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c -- cgit v1.2.3-70-g09d2 From b8f8e559c71980c527d8697d274d53809c61aa37 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jun 2018 08:47:20 -0400 Subject: media: coda: mark CODA960 firmware version 2.1.9 as supported This patch adds the i.MX6 CODA960 firmware versions 2.1.9 (revision 32515) to the list of supported firmware versions. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 92044b779f59..26d14e6a64cc 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -722,6 +722,7 @@ static u32 coda_supported_firmwares[] = { CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), }; -- cgit v1.2.3-70-g09d2 From 662a99e145661c2b35155cf375044deae9b79896 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 29 Jun 2018 17:49:22 -0400 Subject: media: fsl-viu: fix error handling in viu_of_probe() viu_of_probe() ignores fails in i2c_get_adapter(), tries to unlock uninitialized mutex on error path. The patch streamlining the error handling in viu_of_probe(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index e41510ce69a4..0273302aa741 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1414,7 +1414,7 @@ static int viu_of_probe(struct platform_device *op) sizeof(struct viu_reg), DRV_NAME)) { dev_err(&op->dev, "Error while requesting mem region\n"); ret = -EBUSY; - goto err; + goto err_irq; } /* remap registers */ @@ -1422,7 +1422,7 @@ static int viu_of_probe(struct platform_device *op) if (!viu_regs) { dev_err(&op->dev, "Can't map register set\n"); ret = -ENOMEM; - goto err; + goto err_irq; } /* Prepare our private structure */ @@ -1430,7 +1430,7 @@ static int viu_of_probe(struct platform_device *op) if (!viu_dev) { dev_err(&op->dev, "Can't allocate private structure\n"); ret = -ENOMEM; - goto err; + goto err_irq; } viu_dev->vr = viu_regs; @@ -1446,16 +1446,21 @@ static int viu_of_probe(struct platform_device *op) ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); if (ret < 0) { dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); - goto err; + goto err_irq; } ad = i2c_get_adapter(0); + if (!ad) { + ret = -EFAULT; + dev_err(&op->dev, "couldn't get i2c adapter\n"); + goto err_v4l2; + } v4l2_ctrl_handler_init(&viu_dev->hdl, 5); if (viu_dev->hdl.error) { ret = viu_dev->hdl.error; dev_err(&op->dev, "couldn't register control\n"); - goto err_vdev; + goto err_i2c; } /* This control handler will inherit the control(s) from the sub-device(s). */ @@ -1471,7 +1476,7 @@ static int viu_of_probe(struct platform_device *op) vdev = video_device_alloc(); if (vdev == NULL) { ret = -ENOMEM; - goto err_vdev; + goto err_hdl; } *vdev = viu_template; @@ -1492,7 +1497,7 @@ static int viu_of_probe(struct platform_device *op) ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { video_device_release(viu_dev->vdev); - goto err_vdev; + goto err_unlock; } /* enable VIU clock */ @@ -1500,12 +1505,12 @@ static int viu_of_probe(struct platform_device *op) if (IS_ERR(clk)) { dev_err(&op->dev, "failed to lookup the clock!\n"); ret = PTR_ERR(clk); - goto err_clk; + goto err_vdev; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&op->dev, "failed to enable the clock!\n"); - goto err_clk; + goto err_vdev; } viu_dev->clk = clk; @@ -1516,7 +1521,7 @@ static int viu_of_probe(struct platform_device *op) if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { dev_err(&op->dev, "Request VIU IRQ failed.\n"); ret = -ENODEV; - goto err_irq; + goto err_clk; } mutex_unlock(&viu_dev->lock); @@ -1524,16 +1529,19 @@ static int viu_of_probe(struct platform_device *op) dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); return ret; -err_irq: - clk_disable_unprepare(viu_dev->clk); err_clk: - video_unregister_device(viu_dev->vdev); + clk_disable_unprepare(viu_dev->clk); err_vdev: - v4l2_ctrl_handler_free(&viu_dev->hdl); + video_unregister_device(viu_dev->vdev); +err_unlock: mutex_unlock(&viu_dev->lock); +err_hdl: + v4l2_ctrl_handler_free(&viu_dev->hdl); +err_i2c: i2c_put_adapter(ad); +err_v4l2: v4l2_device_unregister(&viu_dev->v4l2_dev); -err: +err_irq: irq_dispose_mapping(viu_irq); return ret; } -- cgit v1.2.3-70-g09d2 From 0245abf8206fb573a3977558de2b7b2be6f3d9f8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 29 Jun 2018 06:12:43 -0400 Subject: media: v4l2-ctrls.c: fix broken auto cluster handling When you switch from auto to manual mode for an auto-cluster (e.g. autogain+gain controls), then the current HW value has to be copied to the current control value. However, has_changed was never set to true, so new_to_cur didn't actually copy this value. Reported-by: Hugues FRUCHET Tested-by: Hugues FRUCHET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d29e45516eb7..d1087573da34 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3137,9 +3137,22 @@ static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master, /* If OK, then make the new values permanent. */ update_flag = is_cur_manual(master) != is_new_manual(master); - for (i = 0; i < master->ncontrols; i++) + + for (i = 0; i < master->ncontrols; i++) { + /* + * If we switch from auto to manual mode, and this cluster + * contains volatile controls, then all non-master controls + * have to be marked as changed. The 'new' value contains + * the volatile value (obtained by update_from_auto_cluster), + * which now has to become the current value. + */ + if (i && update_flag && is_new_manual(master) && + master->has_volatiles && master->cluster[i]) + master->cluster[i]->has_changed = true; + new_to_cur(fh, master->cluster[i], ch_flags | ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + } return 0; } -- cgit v1.2.3-70-g09d2 From 4d1e4545a65903a09f5d15d32a3fbb6131a8d11e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Jul 2018 08:43:02 -0400 Subject: media: mark entity-intf links as IMMUTABLE Currently links between entities and an interface are just marked as ENABLED. But (at least today) these links cannot be disabled by userspace or the driver, so they should also be marked as IMMUTABLE. It might become possible that drivers can disable such links (if for some reason the device node cannot be used), so we might need to add a new link flag at some point to mark interface links that can be changed by the driver but not by userspace. Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvbdev.c | 18 ++++++++++++------ drivers/media/v4l2-core/v4l2-dev.c | 3 ++- drivers/media/v4l2-core/v4l2-device.c | 3 ++- 3 files changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 64d6793674b9..3c8778570331 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -440,8 +440,10 @@ static int dvb_register_media_device(struct dvb_device *dvbdev, if (!dvbdev->entity) return 0; - link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + link = media_create_intf_link(dvbdev->entity, + &dvbdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; #endif @@ -599,7 +601,8 @@ static int dvb_create_io_intf_links(struct dvb_adapter *adap, if (strncmp(entity->name, name, strlen(name))) continue; link = media_create_intf_link(entity, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -754,14 +757,16 @@ int dvb_create_media_graph(struct dvb_adapter *adap, media_device_for_each_intf(intf, mdev) { if (intf->type == MEDIA_INTF_T_DVB_CA && ca) { link = media_create_intf_link(ca, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) { link = media_create_intf_link(tuner, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -773,7 +778,8 @@ int dvb_create_media_graph(struct dvb_adapter *adap, */ if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) { link = media_create_intf_link(demux, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 4ffd7d60a901..5f43f63fa700 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -808,7 +808,8 @@ static int video_register_media_controller(struct video_device *vdev, int type) link = media_create_intf_link(&vdev->entity, &vdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) { media_devnode_remove(vdev->intf_devnode); media_device_unregister_entity(&vdev->entity); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 937c6de85606..3940e55c72f1 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -267,7 +267,8 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) link = media_create_intf_link(&sd->entity, &vdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) { err = -ENOMEM; goto clean_up; -- cgit v1.2.3-70-g09d2 From be2fff656322e82f215730839063c2c2ca73d14b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 2 Jul 2018 11:36:05 -0400 Subject: media: add helpers for memory-to-memory media controller A memory-to-memory pipeline device consists in three entities: two DMA engine and one video processing entities. The DMA engine entities are linked to a V4L interface. This commit add a new v4l2_m2m_{un}register_media_controller API to register this topology. For instance, a typical mem2mem device topology would look like this: Device topology - entity 1: source (1 pad, 1 link) type Node subtype V4L flags 0 pad0: Source -> "proc":1 [ENABLED,IMMUTABLE] - entity 3: proc (2 pads, 2 links) type Node subtype Unknown flags 0 pad0: Source -> "sink":0 [ENABLED,IMMUTABLE] pad1: Sink <- "source":0 [ENABLED,IMMUTABLE] - entity 6: sink (1 pad, 1 link) type Node subtype V4L flags 0 pad0: Sink <- "proc":0 [ENABLED,IMMUTABLE] [hans.verkuil@cisco.com: mark interface links as IMMUTABLE] Suggested-by: Laurent Pinchart Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 13 ++- drivers/media/v4l2-core/v4l2-mem2mem.c | 190 +++++++++++++++++++++++++++++++++ include/media/v4l2-mem2mem.h | 19 ++++ 3 files changed, 217 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 5f43f63fa700..69e775930fc4 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -202,7 +202,7 @@ static void v4l2_device_release(struct device *cd) mutex_unlock(&videodev_lock); #if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev->mdev) { + if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) { /* Remove interfaces and interface links */ media_devnode_remove(vdev->intf_devnode); if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) @@ -733,19 +733,22 @@ static void determine_valid_ioctls(struct video_device *vdev) BASE_VIDIOC_PRIVATE); } -static int video_register_media_controller(struct video_device *vdev, int type) +static int video_register_media_controller(struct video_device *vdev) { #if defined(CONFIG_MEDIA_CONTROLLER) u32 intf_type; int ret; - if (!vdev->v4l2_dev->mdev) + /* Memory-to-memory devices are more complex and use + * their own function to register its mc entities. + */ + if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) return 0; vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; vdev->entity.function = MEDIA_ENT_F_UNKNOWN; - switch (type) { + switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: intf_type = MEDIA_INTF_T_V4L_VIDEO; vdev->entity.function = MEDIA_ENT_F_IO_V4L; @@ -994,7 +997,7 @@ int __video_register_device(struct video_device *vdev, v4l2_device_get(vdev->v4l2_dev); /* Part 5: Register the entity. */ - ret = video_register_media_controller(vdev, type); + ret = video_register_media_controller(vdev); /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 5f9cd5b74cda..725da74d15d8 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -17,9 +17,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -50,6 +52,17 @@ module_param(debug, bool, 0644); * offsets but for different queues */ #define DST_QUEUE_OFF_BASE (1 << 30) +enum v4l2_m2m_entity_type { + MEM2MEM_ENT_TYPE_SOURCE, + MEM2MEM_ENT_TYPE_SINK, + MEM2MEM_ENT_TYPE_PROC +}; + +static const char * const m2m_entity_name[] = { + "source", + "sink", + "proc" +}; /** * struct v4l2_m2m_dev - per-device context @@ -60,6 +73,15 @@ module_param(debug, bool, 0644); */ struct v4l2_m2m_dev { struct v4l2_m2m_ctx *curr_ctx; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_entity *source; + struct media_pad source_pad; + struct media_entity sink; + struct media_pad sink_pad; + struct media_entity proc; + struct media_pad proc_pads[2]; + struct media_intf_devnode *intf_devnode; +#endif struct list_head job_queue; spinlock_t job_spinlock; @@ -594,6 +616,174 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_mmap); +#if defined(CONFIG_MEDIA_CONTROLLER) +void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev) +{ + media_remove_intf_links(&m2m_dev->intf_devnode->intf); + media_devnode_remove(m2m_dev->intf_devnode); + + media_entity_remove_links(m2m_dev->source); + media_entity_remove_links(&m2m_dev->sink); + media_entity_remove_links(&m2m_dev->proc); + media_device_unregister_entity(m2m_dev->source); + media_device_unregister_entity(&m2m_dev->sink); + media_device_unregister_entity(&m2m_dev->proc); + kfree(m2m_dev->source->name); + kfree(m2m_dev->sink.name); + kfree(m2m_dev->proc.name); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_unregister_media_controller); + +static int v4l2_m2m_register_entity(struct media_device *mdev, + struct v4l2_m2m_dev *m2m_dev, enum v4l2_m2m_entity_type type, + struct video_device *vdev, int function) +{ + struct media_entity *entity; + struct media_pad *pads; + char *name; + unsigned int len; + int num_pads; + int ret; + + switch (type) { + case MEM2MEM_ENT_TYPE_SOURCE: + entity = m2m_dev->source; + pads = &m2m_dev->source_pad; + pads[0].flags = MEDIA_PAD_FL_SOURCE; + num_pads = 1; + break; + case MEM2MEM_ENT_TYPE_SINK: + entity = &m2m_dev->sink; + pads = &m2m_dev->sink_pad; + pads[0].flags = MEDIA_PAD_FL_SINK; + num_pads = 1; + break; + case MEM2MEM_ENT_TYPE_PROC: + entity = &m2m_dev->proc; + pads = m2m_dev->proc_pads; + pads[0].flags = MEDIA_PAD_FL_SINK; + pads[1].flags = MEDIA_PAD_FL_SOURCE; + num_pads = 2; + break; + default: + return -EINVAL; + } + + entity->obj_type = MEDIA_ENTITY_TYPE_BASE; + if (type != MEM2MEM_ENT_TYPE_PROC) { + entity->info.dev.major = VIDEO_MAJOR; + entity->info.dev.minor = vdev->minor; + } + len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]); + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]); + entity->name = name; + entity->function = function; + + ret = media_entity_pads_init(entity, num_pads, pads); + if (ret) + return ret; + ret = media_device_register_entity(mdev, entity); + if (ret) + return ret; + + return 0; +} + +int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function) +{ + struct media_device *mdev = vdev->v4l2_dev->mdev; + struct media_link *link; + int ret; + + if (!mdev) + return 0; + + /* A memory-to-memory device consists in two + * DMA engine and one video processing entities. + * The DMA engine entities are linked to a V4L interface + */ + + /* Create the three entities with their pads */ + m2m_dev->source = &vdev->entity; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_SOURCE, vdev, MEDIA_ENT_F_IO_V4L); + if (ret) + return ret; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_PROC, vdev, function); + if (ret) + goto err_rel_entity0; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_SINK, vdev, MEDIA_ENT_F_IO_V4L); + if (ret) + goto err_rel_entity1; + + /* Connect the three entities */ + ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 1, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rel_entity2; + + ret = media_create_pad_link(&m2m_dev->proc, 0, &m2m_dev->sink, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rm_links0; + + /* Create video interface */ + m2m_dev->intf_devnode = media_devnode_create(mdev, + MEDIA_INTF_T_V4L_VIDEO, 0, + VIDEO_MAJOR, vdev->minor); + if (!m2m_dev->intf_devnode) { + ret = -ENOMEM; + goto err_rm_links1; + } + + /* Connect the two DMA engines to the interface */ + link = media_create_intf_link(m2m_dev->source, + &m2m_dev->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_devnode; + } + + link = media_create_intf_link(&m2m_dev->sink, + &m2m_dev->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_intf_link; + } + return 0; + +err_rm_intf_link: + media_remove_intf_links(&m2m_dev->intf_devnode->intf); +err_rm_devnode: + media_devnode_remove(m2m_dev->intf_devnode); +err_rm_links1: + media_entity_remove_links(&m2m_dev->sink); +err_rm_links0: + media_entity_remove_links(&m2m_dev->proc); + media_entity_remove_links(m2m_dev->source); +err_rel_entity2: + media_device_unregister_entity(&m2m_dev->proc); + kfree(m2m_dev->proc.name); +err_rel_entity1: + media_device_unregister_entity(&m2m_dev->sink); + kfree(m2m_dev->sink.name); +err_rel_entity0: + media_device_unregister_entity(m2m_dev->source); + kfree(m2m_dev->source->name); + return ret; + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_register_media_controller); +#endif + struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 8f4b208cfee7..af48b1eca025 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -47,6 +47,7 @@ struct v4l2_m2m_ops { void (*job_abort)(void *priv); }; +struct video_device; struct v4l2_m2m_dev; /** @@ -322,6 +323,24 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, */ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops); +#if defined(CONFIG_MEDIA_CONTROLLER) +void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev); +int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function); +#else +static inline void +v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev) +{ +} + +static inline int +v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function) +{ + return 0; +} +#endif + /** * v4l2_m2m_release() - cleans up and frees a m2m_dev structure * -- cgit v1.2.3-70-g09d2 From e35f702151346b7a97979d9d78d66ab461ba366f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Jul 2018 11:36:06 -0400 Subject: media: vim2m: add media device Request API requires a media node. Add one to the vim2m driver so we can use requests with it. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 1393aa806462..462099a141e4 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -140,6 +140,9 @@ static struct vim2m_fmt *find_format(struct v4l2_format *f) struct vim2m_dev { struct v4l2_device v4l2_dev; struct video_device vfd; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif atomic_t num_inst; struct mutex dev_mutex; @@ -1016,7 +1019,7 @@ static int vim2m_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto unreg_dev; + goto unreg_v4l2; } video_set_drvdata(vfd, dev); @@ -1030,15 +1033,39 @@ static int vim2m_probe(struct platform_device *pdev) if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto err_m2m; + goto unreg_dev; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &pdev->dev; + strlcpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->v4l2_dev.mdev = &dev->mdev; + + ret = v4l2_m2m_register_media_controller(dev->m2m_dev, + vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m; } + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); + goto unreg_m2m_mc; + } +#endif return 0; -err_m2m: +#ifdef CONFIG_MEDIA_CONTROLLER +unreg_m2m_mc: + v4l2_m2m_unregister_media_controller(dev->m2m_dev); +unreg_m2m: v4l2_m2m_release(dev->m2m_dev); - video_unregister_device(&dev->vfd); +#endif unreg_dev: + video_unregister_device(&dev->vfd); +unreg_v4l2: v4l2_device_unregister(&dev->v4l2_dev); return ret; @@ -1049,6 +1076,12 @@ static int vim2m_remove(struct platform_device *pdev) struct vim2m_dev *dev = platform_get_drvdata(pdev); v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->m2m_dev); + media_device_cleanup(&dev->mdev); +#endif v4l2_m2m_release(dev->m2m_dev); del_timer_sync(&dev->timer); video_unregister_device(&dev->vfd); -- cgit v1.2.3-70-g09d2 From d24c029ed3fb64f4b61ff38b8db4e2b35fa140a8 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:23 -0400 Subject: media: rcar-vin: Rename 'digital' to 'parallel' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the term 'digital' is used all over the rcar-vin code in place of 'parallel', rename all the occurrencies. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 72 ++++++++++++++--------------- drivers/media/platform/rcar-vin/rcar-dma.c | 4 +- drivers/media/platform/rcar-vin/rcar-v4l2.c | 12 ++--- drivers/media/platform/rcar-vin/rcar-vin.h | 6 +-- 4 files changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index e6a010ee4ba1..2a8ad99777ec 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -376,12 +376,12 @@ static int rvin_find_pad(struct v4l2_subdev *sd, int direction) } /* ----------------------------------------------------------------------------- - * Digital async notifier + * Parallel async notifier */ /* The vin lock should be held when calling the subdevice attach and detach */ -static int rvin_digital_subdevice_attach(struct rvin_dev *vin, - struct v4l2_subdev *subdev) +static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, + struct v4l2_subdev *subdev) { struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -392,15 +392,15 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); if (ret < 0) return ret; - vin->digital->source_pad = ret; + vin->parallel->source_pad = ret; ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->digital->sink_pad = ret < 0 ? 0 : ret; + vin->parallel->sink_pad = ret < 0 ? 0 : ret; /* Find compatible subdevices mbus format */ vin->mbus_code = 0; code.index = 0; - code.pad = vin->digital->source_pad; + code.pad = vin->parallel->source_pad; while (!vin->mbus_code && !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { code.index++; @@ -450,21 +450,21 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, vin->vdev.ctrl_handler = &vin->ctrl_handler; - vin->digital->subdev = subdev; + vin->parallel->subdev = subdev; return 0; } -static void rvin_digital_subdevice_detach(struct rvin_dev *vin) +static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) { rvin_v4l2_unregister(vin); v4l2_ctrl_handler_free(&vin->ctrl_handler); vin->vdev.ctrl_handler = NULL; - vin->digital->subdev = NULL; + vin->parallel->subdev = NULL; } -static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) +static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) { struct rvin_dev *vin = notifier_to_vin(notifier); int ret; @@ -478,28 +478,28 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) return rvin_v4l2_register(vin); } -static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { struct rvin_dev *vin = notifier_to_vin(notifier); - vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); mutex_lock(&vin->lock); - rvin_digital_subdevice_detach(vin); + rvin_parallel_subdevice_detach(vin); mutex_unlock(&vin->lock); } -static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { struct rvin_dev *vin = notifier_to_vin(notifier); int ret; mutex_lock(&vin->lock); - ret = rvin_digital_subdevice_attach(vin, subdev); + ret = rvin_parallel_subdevice_attach(vin, subdev); mutex_unlock(&vin->lock); if (ret) return ret; @@ -507,21 +507,21 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->digital->source_pad, - vin->digital->sink_pad); + subdev->name, vin->parallel->source_pad, + vin->parallel->sink_pad); return 0; } -static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = { - .bound = rvin_digital_notify_bound, - .unbind = rvin_digital_notify_unbind, - .complete = rvin_digital_notify_complete, +static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { + .bound = rvin_parallel_notify_bound, + .unbind = rvin_parallel_notify_unbind, + .complete = rvin_parallel_notify_complete, }; -static int rvin_digital_parse_v4l2(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int rvin_parallel_parse_v4l2(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) { struct rvin_dev *vin = dev_get_drvdata(dev); struct rvin_graph_entity *rvge = @@ -546,28 +546,28 @@ static int rvin_digital_parse_v4l2(struct device *dev, return -EINVAL; } - vin->digital = rvge; + vin->parallel = rvge; return 0; } -static int rvin_digital_graph_init(struct rvin_dev *vin) +static int rvin_parallel_graph_init(struct rvin_dev *vin) { int ret; ret = v4l2_async_notifier_parse_fwnode_endpoints( vin->dev, &vin->notifier, - sizeof(struct rvin_graph_entity), rvin_digital_parse_v4l2); + sizeof(struct rvin_graph_entity), rvin_parallel_parse_v4l2); if (ret) return ret; - if (!vin->digital) + if (!vin->parallel) return -ENODEV; - vin_dbg(vin, "Found digital subdevice %pOF\n", - to_of_node(vin->digital->asd.match.fwnode)); + vin_dbg(vin, "Found parallel subdevice %pOF\n", + to_of_node(vin->parallel->asd.match.fwnode)); - vin->notifier.ops = &rvin_digital_notify_ops; + vin->notifier.ops = &rvin_parallel_notify_ops; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); @@ -1136,7 +1136,7 @@ static int rcar_vin_probe(struct platform_device *pdev) if (vin->info->use_mc) ret = rvin_mc_init(vin); else - ret = rvin_digital_graph_init(vin); + ret = rvin_parallel_graph_init(vin); if (ret < 0) goto error; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index cfe5d7a9d44e..ce9502f15f5b 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -733,7 +733,7 @@ static int rvin_setup(struct rvin_dev *vin) vnmc |= VNMC_BPS; if (vin->info->model == RCAR_GEN3) { - /* Select between CSI-2 and Digital input */ + /* Select between CSI-2 and parallel input */ if (vin->mbus_cfg.type == V4L2_MBUS_CSI2) vnmc &= ~VNMC_DPINE; else @@ -1088,7 +1088,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) /* No media controller used, simply pass operation to subdevice. */ if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->digital->subdev, video, s_stream, + ret = v4l2_subdev_call(vin->parallel->subdev, video, s_stream, on); return ret == -ENOIOCTLCMD ? 0 : ret; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index e78fba84d590..87a718b1c2e6 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -144,7 +144,7 @@ static int rvin_reset_format(struct rvin_dev *vin) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; int ret; @@ -175,7 +175,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; enum v4l2_field field; u32 width, height; @@ -517,7 +517,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, if (timings->pad) return -EINVAL; - timings->pad = vin->digital->sink_pad; + timings->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -569,7 +569,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, if (cap->pad) return -EINVAL; - cap->pad = vin->digital->sink_pad; + cap->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -587,7 +587,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, get_edid, edid); @@ -605,7 +605,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, set_edid, edid); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index ff747e22d8cf..b89f63b8ecdc 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -148,7 +148,7 @@ struct rvin_info { * @v4l2_dev: V4L2 device * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @digital: entity in the DT for local digital subdevice + * @parallel: entity in the DT for local parallel subdevice * * @group: Gen3 CSI group * @id: Gen3 group id for this VIN @@ -184,7 +184,7 @@ struct rvin_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity *digital; + struct rvin_graph_entity *parallel; struct rvin_group *group; unsigned int id; @@ -211,7 +211,7 @@ struct rvin_dev { v4l2_std_id std; }; -#define vin_to_source(vin) ((vin)->digital->subdev) +#define vin_to_source(vin) ((vin)->parallel->subdev) /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) -- cgit v1.2.3-70-g09d2 From a561e9e0944aea3e8683c3ab17219d6942ad3ca8 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:24 -0400 Subject: media: rcar-vin: Remove two empty lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove un-necessary empty lines. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 2a8ad99777ec..1548b409806d 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -707,11 +707,9 @@ static int rvin_mc_parse_of_endpoint(struct device *dev, return -EINVAL; if (!of_device_is_available(to_of_node(asd->match.fwnode))) { - vin_dbg(vin, "OF device %pOF disabled, ignoring\n", to_of_node(asd->match.fwnode)); return -ENOTCONN; - } if (vin->group->csi[vep->base.id].fwnode) { -- cgit v1.2.3-70-g09d2 From 2241ea75b82c43e060d3da26fa2c286f6c872348 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:25 -0400 Subject: media: rcar-vin: Create a group notifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As CSI-2 subdevices are shared between several VIN instances, a shared notifier to collect the CSI-2 async subdevices is required. So far, the rcar-vin driver used the notifier of the last VIN instance to probe but with the forth-coming introduction of parallel input subdevices support in mc-compliant code path, each VIN may register its own notifier if any parallel subdevice is connected there. To avoid registering a notifier twice (once for parallel subdev and one for the CSI-2 subdevs) create a group notifier, shared by all the VIN instances. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 46 +++++++++++++---------------- drivers/media/platform/rcar-vin/rcar-vin.h | 5 ++-- 2 files changed, 23 insertions(+), 28 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 1548b409806d..735e6ac66e48 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -46,6 +46,8 @@ */ #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) +#define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) + /* ----------------------------------------------------------------------------- * Media Controller link notification */ @@ -359,8 +361,6 @@ out: * Async notifier */ -#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) - static int rvin_find_pad(struct v4l2_subdev *sd, int direction) { unsigned int pad; @@ -466,7 +466,7 @@ static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); @@ -482,7 +482,7 @@ static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); @@ -495,7 +495,7 @@ static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; mutex_lock(&vin->lock); @@ -583,7 +583,7 @@ static int rvin_parallel_graph_init(struct rvin_dev *vin) static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); const struct rvin_group_route *route; unsigned int i; int ret; @@ -649,7 +649,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; for (i = 0; i < RCAR_VIN_NUM; i++) @@ -673,7 +673,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; mutex_lock(&vin->group->lock); @@ -734,12 +734,6 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_lock(&vin->group->lock); - /* If there already is a notifier something has gone wrong, bail out. */ - if (WARN_ON(vin->group->notifier)) { - mutex_unlock(&vin->group->lock); - return -EINVAL; - } - /* If not all VIN's are registered don't register the notifier. */ for (i = 0; i < RCAR_VIN_NUM; i++) if (vin->group->vin[i]) @@ -751,19 +745,16 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) } /* - * Have all VIN's look for subdevices. Some subdevices will overlap - * but the parser function can handle it, so each subdevice will - * only be registered once with the notifier. + * Have all VIN's look for CSI-2 subdevices. Some subdevices will + * overlap but the parser function can handle it, so each subdevice + * will only be registered once with the group notifier. */ - - vin->group->notifier = &vin->notifier; - for (i = 0; i < RCAR_VIN_NUM; i++) { if (!vin->group->vin[i]) continue; ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( - vin->group->vin[i]->dev, vin->group->notifier, + vin->group->vin[i]->dev, &vin->group->notifier, sizeof(struct v4l2_async_subdev), 1, rvin_mc_parse_of_endpoint); if (ret) { @@ -774,9 +765,12 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_unlock(&vin->group->lock); - vin->group->notifier->ops = &rvin_group_notify_ops; + if (!vin->group->notifier.num_subdevs) + return 0; - ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + vin->group->notifier.ops = &rvin_group_notify_ops; + ret = v4l2_async_notifier_register(&vin->v4l2_dev, + &vin->group->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); return ret; @@ -1162,8 +1156,10 @@ static int rcar_vin_remove(struct platform_device *pdev) if (vin->info->use_mc) { mutex_lock(&vin->group->lock); - if (vin->group->notifier == &vin->notifier) - vin->group->notifier = NULL; + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } mutex_unlock(&vin->group->lock); rvin_group_put(vin); } else { diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index b89f63b8ecdc..17b4de4d2734 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -227,8 +227,7 @@ struct rvin_dev { * * @lock: protects the count, notifier, vin and csi members * @count: number of enabled VIN instances found in DT - * @notifier: pointer to the notifier of a VIN which handles the - * groups async sub-devices. + * @notifier: group notifier for CSI-2 async subdevices * @vin: VIN instances which are part of the group * @csi: array of pairs of fwnode and subdev pointers * to all CSI-2 subdevices. @@ -240,7 +239,7 @@ struct rvin_group { struct mutex lock; unsigned int count; - struct v4l2_async_notifier *notifier; + struct v4l2_async_notifier notifier; struct rvin_dev *vin[RCAR_VIN_NUM]; struct { -- cgit v1.2.3-70-g09d2 From 9863bc8695bc36e39aca289650e5a4f17c1bf628 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:26 -0400 Subject: media: rcar-vin: Cleanup notifier in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the notifier initialization, memory for the list of associated async subdevices is reserved during the fwnode endpoint parsing from the v4l2-async framework. If the notifier registration fails, that memory should be released and the notifier 'cleaned up'. Catch the notifier registration error and perform the cleanup both for the group and the parallel notifiers. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 735e6ac66e48..87b29be2ea9e 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -571,6 +571,7 @@ static int rvin_parallel_graph_init(struct rvin_dev *vin) ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } @@ -773,6 +774,7 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) &vin->group->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } -- cgit v1.2.3-70-g09d2 From 158e2a53fc9620fac7ebbb83223ec18280bd34f0 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:27 -0400 Subject: media: rcar-vin: Cache the mbus configuration flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Media bus configuration flags and media bus type were so far a property of each VIN instance, as the subdevice they were connected to was immutable during the whole system life time. With the forth-coming introduction of parallel input devices support, a VIN instance can have the subdevice it is connected to switched at runtime, from a CSI-2 subdevice to a parallel one and viceversa, through the modification of links between media entities in the media controller graph. To avoid discarding the per-subdevice configuration flags retrieved by v4l2_fwnode parsing facilities, cache them in the 'rvin_graph_entity' member of each VIN instance, opportunely renamed to 'rvin_parallel_entity'. Also modify the register configuration function to take mbus flags into account when running on a bus type that supports them. The media bus type currently in use will be updated in a follow-up patch to the link state change notification function. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 21 ++++++++----------- drivers/media/platform/rcar-vin/rcar-dma.c | 32 +++++++++++++++++++---------- drivers/media/platform/rcar-vin/rcar-vin.h | 22 ++++++++++++++------ 3 files changed, 45 insertions(+), 30 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 87b29be2ea9e..7f84c7b9518a 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -524,30 +524,29 @@ static int rvin_parallel_parse_v4l2(struct device *dev, struct v4l2_async_subdev *asd) { struct rvin_dev *vin = dev_get_drvdata(dev); - struct rvin_graph_entity *rvge = - container_of(asd, struct rvin_graph_entity, asd); + struct rvin_parallel_entity *rvpe = + container_of(asd, struct rvin_parallel_entity, asd); if (vep->base.port || vep->base.id) return -ENOTCONN; - vin->mbus_cfg.type = vep->bus_type; + vin->parallel = rvpe; + vin->parallel->mbus_type = vep->bus_type; - switch (vin->mbus_cfg.type) { + switch (vin->parallel->mbus_type) { case V4L2_MBUS_PARALLEL: vin_dbg(vin, "Found PARALLEL media bus\n"); - vin->mbus_cfg.flags = vep->bus.parallel.flags; + vin->parallel->mbus_flags = vep->bus.parallel.flags; break; case V4L2_MBUS_BT656: vin_dbg(vin, "Found BT656 media bus\n"); - vin->mbus_cfg.flags = 0; + vin->parallel->mbus_flags = 0; break; default: vin_err(vin, "Unknown media bus type\n"); return -EINVAL; } - vin->parallel = rvge; - return 0; } @@ -557,7 +556,7 @@ static int rvin_parallel_graph_init(struct rvin_dev *vin) ret = v4l2_async_notifier_parse_fwnode_endpoints( vin->dev, &vin->notifier, - sizeof(struct rvin_graph_entity), rvin_parallel_parse_v4l2); + sizeof(struct rvin_parallel_entity), rvin_parallel_parse_v4l2); if (ret) return ret; @@ -785,10 +784,6 @@ static int rvin_mc_init(struct rvin_dev *vin) { int ret; - /* All our sources are CSI-2 */ - vin->mbus_cfg.type = V4L2_MBUS_CSI2; - vin->mbus_cfg.flags = 0; - vin->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); if (ret) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index ce9502f15f5b..1de598120286 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -659,8 +659,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV8_BT656; + else + vnmc |= VNMC_INF_YUV8_BT601; + input_is_yuv = true; break; case MEDIA_BUS_FMT_RGB888_1X24: @@ -668,8 +672,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV10_BT656; + else + vnmc |= VNMC_INF_YUV10_BT601; + input_is_yuv = true; break; default: @@ -682,13 +690,15 @@ static int rvin_setup(struct rvin_dev *vin) else dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); - /* Hsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_HPS; + if (!vin->is_csi) { + /* Hsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; - /* Vsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_VPS; + /* Vsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; + } /* * Output format @@ -734,7 +744,7 @@ static int rvin_setup(struct rvin_dev *vin) if (vin->info->model == RCAR_GEN3) { /* Select between CSI-2 and parallel input */ - if (vin->mbus_cfg.type == V4L2_MBUS_CSI2) + if (vin->is_csi) vnmc &= ~VNMC_DPINE; else vnmc |= VNMC_DPINE; diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 17b4de4d2734..df2a47e4be31 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -75,16 +75,22 @@ struct rvin_video_format { }; /** - * struct rvin_graph_entity - Video endpoint from async framework + * struct rvin_parallel_entity - Parallel video input endpoint descriptor * @asd: sub-device descriptor for async framework * @subdev: subdevice matched using async framework + * @mbus_type: media bus type + * @mbus_flags: media bus configuration flags * @source_pad: source pad of remote subdevice * @sink_pad: sink pad of remote subdevice + * */ -struct rvin_graph_entity { +struct rvin_parallel_entity { struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; + enum v4l2_mbus_type mbus_type; + unsigned int mbus_flags; + unsigned int source_pad; unsigned int sink_pad; }; @@ -148,7 +154,8 @@ struct rvin_info { * @v4l2_dev: V4L2 device * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @parallel: entity in the DT for local parallel subdevice + * + * @parallel: parallel input subdevice descriptor * * @group: Gen3 CSI group * @id: Gen3 group id for this VIN @@ -166,7 +173,8 @@ struct rvin_info { * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state * - * @mbus_cfg: media bus configuration from DT + * @is_csi: flag to mark the VIN as using a CSI-2 subdevice + * * @mbus_code: media bus format code * @format: active V4L2 pixel format * @@ -184,7 +192,8 @@ struct rvin_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity *parallel; + + struct rvin_parallel_entity *parallel; struct rvin_group *group; unsigned int id; @@ -201,7 +210,8 @@ struct rvin_dev { unsigned int sequence; enum rvin_dma_state state; - struct v4l2_mbus_config mbus_cfg; + bool is_csi; + u32 mbus_code; struct v4l2_pix_format format; -- cgit v1.2.3-70-g09d2 From a597a772cd3f51193e1917a729db8a991615341b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:28 -0400 Subject: media: rcar-vin: Parse parallel input on Gen3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rcar-vin driver so far had a mutually exclusive code path for handling parallel and CSI-2 video input subdevices, with only the CSI-2 use case supporting media-controller. As we add support for parallel inputs to Gen3 media-controller compliant code path now parse both port@0 and port@1, handling the media-controller use case in the parallel bound/unbind notifier operations. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 53 +++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 7f84c7b9518a..e9b5b83122e6 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -397,6 +397,11 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); vin->parallel->sink_pad = ret < 0 ? 0 : ret; + if (vin->info->use_mc) { + vin->parallel->subdev = subdev; + return 0; + } + /* Find compatible subdevices mbus format */ vin->mbus_code = 0; code.index = 0; @@ -458,10 +463,12 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) { rvin_v4l2_unregister(vin); - v4l2_ctrl_handler_free(&vin->ctrl_handler); - - vin->vdev.ctrl_handler = NULL; vin->parallel->subdev = NULL; + + if (!vin->info->use_mc) { + v4l2_ctrl_handler_free(&vin->ctrl_handler); + vin->vdev.ctrl_handler = NULL; + } } static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) @@ -550,18 +557,19 @@ static int rvin_parallel_parse_v4l2(struct device *dev, return 0; } -static int rvin_parallel_graph_init(struct rvin_dev *vin) +static int rvin_parallel_init(struct rvin_dev *vin) { int ret; - ret = v4l2_async_notifier_parse_fwnode_endpoints( - vin->dev, &vin->notifier, - sizeof(struct rvin_parallel_entity), rvin_parallel_parse_v4l2); + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( + vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity), + 0, rvin_parallel_parse_v4l2); if (ret) return ret; + /* If using mc, it's fine not to have any input registered. */ if (!vin->parallel) - return -ENODEV; + return vin->info->use_mc ? 0 : -ENODEV; vin_dbg(vin, "Found parallel subdevice %pOF\n", to_of_node(vin->parallel->asd.match.fwnode)); @@ -1122,20 +1130,35 @@ static int rcar_vin_probe(struct platform_device *pdev) return ret; platform_set_drvdata(pdev, vin); - if (vin->info->use_mc) + + if (vin->info->use_mc) { ret = rvin_mc_init(vin); - else - ret = rvin_parallel_graph_init(vin); - if (ret < 0) - goto error; + if (ret) + goto error_dma_unregister; + } + + ret = rvin_parallel_init(vin); + if (ret) + goto error_group_unregister; pm_suspend_ignore_children(&pdev->dev, true); pm_runtime_enable(&pdev->dev); return 0; -error: + +error_group_unregister: + if (vin->info->use_mc) { + mutex_lock(&vin->group->lock); + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } + mutex_unlock(&vin->group->lock); + rvin_group_put(vin); + } + +error_dma_unregister: rvin_dma_unregister(vin); - v4l2_async_notifier_cleanup(&vin->notifier); return ret; } -- cgit v1.2.3-70-g09d2 From a962a80476b06fc6c2a4b7d77bb38b77dd73edac Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:29 -0400 Subject: media: rcar-vin: Link parallel input media entities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running with media-controller link the parallel input media entities with the VIN entities at 'complete' callback time. To create media links the v4l2_device should be registered first. Check if the device is already registered, to avoid double registrations. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index e9b5b83122e6..bf54e23b12eb 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -474,6 +474,8 @@ static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); + struct media_entity *source; + struct media_entity *sink; int ret; ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); @@ -482,7 +484,26 @@ static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - return rvin_v4l2_register(vin); + if (!video_is_registered(&vin->vdev)) { + ret = rvin_v4l2_register(vin); + if (ret < 0) + return ret; + } + + if (!vin->info->use_mc) + return 0; + + /* If we're running with media-controller, link the subdevs. */ + source = &vin->parallel->subdev->entity; + sink = &vin->vdev.entity; + + ret = media_create_pad_link(source, vin->parallel->source_pad, + sink, vin->parallel->sink_pad, 0); + if (ret) + vin_err(vin, "Error adding link from %s to %s: %d\n", + source->name, sink->name, ret); + + return ret; } static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, @@ -604,7 +625,8 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) /* Register all video nodes for the group. */ for (i = 0; i < RCAR_VIN_NUM; i++) { - if (vin->group->vin[i]) { + if (vin->group->vin[i] && + !video_is_registered(&vin->group->vin[i]->vdev)) { ret = rvin_v4l2_register(vin->group->vin[i]); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 1284605dc821cebdc4793772487f65f56c5d0c62 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:30 -0400 Subject: media: rcar-vin: Handle parallel subdev in link_notify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle parallel subdevices in link_notify callback. If the notified link involves a parallel subdevice, do not change routing of the VIN-CSI-2 devices and mark the VIN instance as using a parallel input. If the CSI-2 link setup succeeds instead, mark the VIN instance as using CSI-2. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 35 ++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index bf54e23b12eb..cd37657530db 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -171,9 +171,37 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* Add the new link to the existing mask and check if it works. */ csi_id = rvin_group_entity_to_csi_id(group, link->source->entity); + + if (csi_id == -ENODEV) { + struct v4l2_subdev *sd; + unsigned int i; + + /* + * Make sure the source entity subdevice is registered as + * a parallel input of one of the enabled VINs if it is not + * one of the CSI-2 subdevices. + * + * No hardware configuration required for parallel inputs, + * we can return here. + */ + sd = media_entity_to_v4l2_subdev(link->source->entity); + for (i = 0; i < RCAR_VIN_NUM; i++) { + if (group->vin[i] && group->vin[i]->parallel && + group->vin[i]->parallel->subdev == sd) { + group->vin[i]->is_csi = false; + ret = 0; + goto out; + } + } + + vin_err(vin, "Subdevice %s not registered to any VIN\n", + link->source->entity->name); + ret = -ENODEV; + goto out; + } + channel = rvin_group_csi_pad_to_channel(link->source->index); mask_new = mask & rvin_group_get_mask(vin, csi_id, channel); - vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); if (!mask_new) { @@ -183,6 +211,11 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* New valid CHSEL found, set the new value. */ ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); + if (ret) + goto out; + + vin->is_csi = true; + out: mutex_unlock(&group->lock); -- cgit v1.2.3-70-g09d2 From 5cda0fca7824605d56e4c0d9a96d464d9a93dc33 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:31 -0400 Subject: media: rcar-vin: Rename _rcar_info to rcar_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove leading underscore to align all rcar_group_route structure declarations. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index cd37657530db..636784079921 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1070,7 +1070,7 @@ static const struct rvin_info rcar_info_r8a77965 = { .routes = rcar_info_r8a77965_routes, }; -static const struct rvin_group_route _rcar_info_r8a77970_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) }, { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, @@ -1086,7 +1086,7 @@ static const struct rvin_info rcar_info_r8a77970 = { .use_mc = true, .max_width = 4096, .max_height = 4096, - .routes = _rcar_info_r8a77970_routes, + .routes = rcar_info_r8a77970_routes, }; static const struct of_device_id rvin_of_id_table[] = { -- cgit v1.2.3-70-g09d2 From aa2446ef9e8b33d304bde808ea3dac416af1bd0c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 Jun 2018 05:43:32 -0400 Subject: media: rcar-vin: Add support for R-Car R8A77995 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add R-Car R8A77995 SoC to the rcar-vin supported ones. Signed-off-by: Jacopo Mondi Reviewed-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 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 636784079921..8843367cd339 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1089,6 +1089,18 @@ static const struct rvin_info rcar_info_r8a77970 = { .routes = rcar_info_r8a77970_routes, }; +static const struct rvin_group_route rcar_info_r8a77995_routes[] = { + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77995 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77995_routes, +}; + static const struct of_device_id rvin_of_id_table[] = { { .compatible = "renesas,vin-r8a7778", @@ -1134,6 +1146,10 @@ static const struct of_device_id rvin_of_id_table[] = { .compatible = "renesas,vin-r8a77970", .data = &rcar_info_r8a77970, }, + { + .compatible = "renesas,vin-r8a77995", + .data = &rcar_info_r8a77995, + }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); -- cgit v1.2.3-70-g09d2 From 5520b9467a39d5ec9ce9cd8a9ed01f826b817524 Mon Sep 17 00:00:00 2001 From: Keiichi Watanabe Date: Mon, 18 Jun 2018 03:58:52 -0400 Subject: media: v4l2-ctrl: Change control for VP8 profile to menu control Add a menu control V4L2_CID_MPEG_VIDEO_VP8_PROFILE for VP8 profile and make V4L2_CID_MPEG_VIDEO_VPX_PROFILE an alias of it. This new control is used to select the desired profile for VP8 encoder and query for supported profiles by VP8 encoder/decoder. Though we have originally a control V4L2_CID_MPEG_VIDEO_VPX_PROFILE and its name contains 'VPX', it works only for VP8 because supported profiles usually differ between VP8 and VP9. In addition, this control cannot be used for querying since it is not a menu control but an integer control, which cannot return an arbitrary set of supported profiles. The new control V4L2_CID_MPEG_VIDEO_VP8_PROFILE is a menu control as with controls for other codec profiles. (e.g. H264) In addition, this patch also fixes the use of V4L2_CID_MPEG_VIDEO_VPX_PROFILE in drivers of Qualcomm's venus and Samsung's s5p-mfc. Signed-off-by: Keiichi Watanabe Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 25 +++++++++++++++++++--- drivers/media/platform/qcom/venus/vdec_ctrls.c | 10 +++++---- drivers/media/platform/qcom/venus/venc.c | 4 ++-- drivers/media/platform/qcom/venus/venc_ctrls.c | 10 +++++---- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 15 ++++++------- drivers/media/v4l2-core/v4l2-ctrls.c | 12 ++++++++++- include/uapi/linux/v4l2-controls.h | 11 +++++++++- 7 files changed, 64 insertions(+), 23 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 03931f9b1285..01ef31a934b4 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1955,9 +1955,28 @@ enum v4l2_vp8_golden_frame_sel - ``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` Quantization parameter for a P frame for VP8. -``V4L2_CID_MPEG_VIDEO_VPX_PROFILE (integer)`` - Select the desired profile for VPx encoder. Acceptable values are 0, - 1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3. +.. _v4l2-mpeg-video-vp8-profile: + +``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp8_profile - + This control allows selecting the profile for VP8 encoder. + This is also used to enumerate supported profiles by VP8 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` + - Profile 3 High Efficiency Video Coding (HEVC/H.265) Control Reference diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 032839bbc967..f4604b0cd57e 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -29,7 +29,7 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: @@ -54,7 +54,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ret = hfi_session_get_property(inst, ptype, &hprop); if (!ret) ctr->profile = hprop.profile_level.profile; @@ -130,8 +130,10 @@ int vdec_ctrl_init(struct venus_inst *inst) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 11dafc7848c5..f7a87a3dbb46 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -223,7 +223,7 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: return HFI_H264_ENTROPY_CABAC; } - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: switch (value) { case 0: default: @@ -754,7 +754,7 @@ static int venc_set_properties(struct venus_inst *inst) level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, ctr->level.h264); } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE, ctr->profile.vpx); level = 0; } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 21e938a28662..459101728d26 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -101,7 +101,7 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: ctr->profile.h264 = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile.vpx = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: @@ -248,6 +248,11 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT); @@ -256,9 +261,6 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT_PEAK); - v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); - v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 570f391f2cfd..3ad4f5073002 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -692,12 +692,12 @@ static struct mfc_control controls[] = { .default_value = 10, }, { - .id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, + .id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_VP8_PROFILE_0, + .maximum = V4L2_MPEG_VIDEO_VP8_PROFILE_3, + .default_value = V4L2_MPEG_VIDEO_VP8_PROFILE_0, + .menu_skip_mask = 0, }, { .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, @@ -2057,7 +2057,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: p->codec.vp8.rc_p_frame_qp = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: p->codec.vp8.profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: @@ -2711,4 +2711,3 @@ void s5p_mfc_enc_init(struct s5p_mfc_ctx *ctx) f.fmt.pix_mp.pixelformat = DEF_DST_FMT_ENC; ctx->dst_fmt = find_format(&f, MFC_FMT_ENC); } - diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d1087573da34..4a182a70de36 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -431,6 +431,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Use Previous Specific Frame", NULL, }; + static const char * const vp8_profile[] = { + "0", + "1", + "2", + "3", + NULL, + }; static const char * const flash_led_mode[] = { "Off", @@ -614,6 +621,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg4_profile; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return vpx_golden_frame_sel; + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + return vp8_profile; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -839,7 +848,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: return "VPX Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: return "VP8 Profile"; /* HEVC controls */ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value"; @@ -1180,6 +1189,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DEINTERLACING_MODE: case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: case V4L2_CID_DETECT_MD_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 8a75ad7899f3..ab96795b2829 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -587,7 +587,16 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) #define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) -#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) + +#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_MPEG_BASE+511) +enum v4l2_mpeg_video_vp8_profile { + V4L2_MPEG_VIDEO_VP8_PROFILE_0 = 0, + V4L2_MPEG_VIDEO_VP8_PROFILE_1 = 1, + V4L2_MPEG_VIDEO_VP8_PROFILE_2 = 2, + V4L2_MPEG_VIDEO_VP8_PROFILE_3 = 3, +}; +/* Deprecated alias for compatibility reasons. */ +#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE V4L2_CID_MPEG_VIDEO_VP8_PROFILE /* CIDs for HEVC encoding. */ -- cgit v1.2.3-70-g09d2 From 2a75364d09b05f257f4cd1f718e06e0247eb1dd3 Mon Sep 17 00:00:00 2001 From: Keiichi Watanabe Date: Mon, 18 Jun 2018 03:58:53 -0400 Subject: media: v4l2-ctrl: Add control for VP9 profile Add a new control V4L2_CID_MPEG_VIDEO_VP9_PROFILE for VP9 profiles. This control allows selecting the desired profile for VP9 encoder and querying for supported profiles by VP9 encoder/decoder. Though this control is similar to V4L2_CID_MPEG_VIDEO_VP8_PROFILE, we need to separate this control from it because supported profiles usually differ between VP8 and VP9. Signed-off-by: Keiichi Watanabe Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 23 ++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 11 +++++++++++ include/uapi/linux/v4l2-controls.h | 7 +++++++ 3 files changed, 41 insertions(+) (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 01ef31a934b4..9f7312bf3365 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1978,6 +1978,29 @@ enum v4l2_mpeg_video_vp8_profile - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` - Profile 3 +.. _v4l2-mpeg-video-vp9-profile: + +``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp9_profile - + This control allows selecting the profile for VP9 encoder. + This is also used to enumerate supported profiles by VP9 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` + - Profile 3 + High Efficiency Video Coding (HEVC/H.265) Control Reference ----------------------------------------------------------- diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4a182a70de36..599c1cbff3b9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -438,6 +438,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "3", NULL, }; + static const char * const vp9_profile[] = { + "0", + "1", + "2", + "3", + NULL, + }; static const char * const flash_led_mode[] = { "Off", @@ -623,6 +630,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return vpx_golden_frame_sel; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: return vp8_profile; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + return vp9_profile; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -849,6 +858,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: return "VP8 Profile"; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: return "VP9 Profile"; /* HEVC controls */ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value"; @@ -1190,6 +1200,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: case V4L2_CID_DETECT_MD_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index ab96795b2829..e4ee10ee917d 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -597,6 +597,13 @@ enum v4l2_mpeg_video_vp8_profile { }; /* Deprecated alias for compatibility reasons. */ #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE V4L2_CID_MPEG_VIDEO_VP8_PROFILE +#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_MPEG_BASE+512) +enum v4l2_mpeg_video_vp9_profile { + V4L2_MPEG_VIDEO_VP9_PROFILE_0 = 0, + V4L2_MPEG_VIDEO_VP9_PROFILE_1 = 1, + V4L2_MPEG_VIDEO_VP9_PROFILE_2 = 2, + V4L2_MPEG_VIDEO_VP9_PROFILE_3 = 3, +}; /* CIDs for HEVC encoding. */ -- cgit v1.2.3-70-g09d2 From d45c9dc0b8349d8005ed8f7bc82c4583f50dd357 Mon Sep 17 00:00:00 2001 From: Keiichi Watanabe Date: Mon, 18 Jun 2018 03:58:54 -0400 Subject: media: mtk-vcodec: Support VP9 profile in decoder Add V4L2_CID_MPEG_VIDEO_VP9_PROFILE control in MediaTek decoder's driver. MediaTek decoder only supports profile 0 for now. Signed-off-by: Keiichi Watanabe Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 5523edadb86c..0c8a8b4c4e1c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1400,6 +1400,11 @@ int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 0, 32, 1, 1); ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, + &mtk_vcodec_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + V4L2_MPEG_VIDEO_VP9_PROFILE_0, + 0, V4L2_MPEG_VIDEO_VP9_PROFILE_0); if (ctx->ctrl_hdl.error) { mtk_v4l2_err("Adding control failed %d", -- cgit v1.2.3-70-g09d2 From c928d45475d7b07f1c1bdd68c327189289fcb3c2 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 2 Jul 2018 17:23:21 -0400 Subject: media: ivtv: zero-initialize cx25840 platform data We need to zero-initialize cx25840 platform data structure to make sure that its future members do not contain random stack garbage. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 522cd111e399..e9ce54dd5e01 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -293,6 +293,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) .platform_data = &pdata, }; + memset(&pdata, 0, sizeof(pdata)); pdata.pvr150_workaround = itv->pvr150_workaround; sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, &cx25840_info, NULL); -- cgit v1.2.3-70-g09d2 From af16d0a132af47c60494c373100ff8952d557b01 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 2 Jul 2018 17:23:22 -0400 Subject: media: cx25840: add kernel-doc description of struct cx25840_state This commit describes a device instance private data of the driver (struct cx25840_state) in a kernel-doc style comment. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-core.h | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h index fb13a624d2e3..c323b1af1f83 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.h +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -45,6 +45,35 @@ enum cx25840_media_pads { CX25840_NUM_PADS }; +/** + * struct cx25840_state - a device instance private data + * @c: i2c_client struct representing this device + * @sd: our V4L2 sub-device + * @hdl: our V4L2 control handler + * @volume: audio volume V4L2 control (non-cx2583x devices only) + * @mute: audio mute V4L2 control (non-cx2583x devices only) + * @pvr150_workaround: whether we enable workaround for Hauppauge PVR150 + * hardware bug (audio dropping out) + * @radio: set if we are currently in the radio mode, otherwise + * the current mode is non-radio (that is, video) + * @std: currently set video standard + * @vid_input: currently set video input + * @aud_input: currently set audio input + * @audclk_freq: currently set audio sample rate + * @audmode: currently set audio mode (when in non-radio mode) + * @vbi_line_offset: vbi line number offset + * @id: exact device model + * @rev: raw device id read from the chip + * @is_initialized: whether we have already loaded firmware into the chip + * and initialized it + * @vbi_regs_offset: offset of vbi regs + * @fw_wait: wait queue to wake an initalization function up when + * firmware loading (on a separate workqueue) finishes + * @fw_work: a work that actually loads the firmware on a separate + * workqueue + * @ir_state: a pointer to chip IR controller private data + * @pads: array of supported chip pads (currently only a stub) + */ struct cx25840_state { struct i2c_client *c; struct v4l2_subdev sd; @@ -66,8 +95,8 @@ struct cx25840_state { u32 rev; int is_initialized; unsigned vbi_regs_offset; - wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ - struct work_struct fw_work; /* work entry for fw load */ + wait_queue_head_t fw_wait; + struct work_struct fw_work; struct cx25840_ir_state *ir_state; #if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pads[CX25840_NUM_PADS]; -- cgit v1.2.3-70-g09d2 From 010b876ae9a05307c138cd591555fcd8a0a0bf14 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 2 Jul 2018 17:23:24 -0400 Subject: media: tuner-simple: allow setting mono radio mode For some types of tuners (Philips FMD1216ME(X) MK3 currently) we know that letting TDA9887 output port 1 remain high (inactive) will switch FM radio to mono mode. Let's make use of this functionality - nothing changes for the default stereo radio mode. Tested on a Medion 95700 board which has a FMD1216ME tuner. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-simple.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index 36b88f820239..29c1473f2e9f 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -670,6 +670,7 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, int rc, j; struct tuner_params *t_params; unsigned int freq = params->frequency; + bool mono = params->audmode == V4L2_TUNER_MODE_MONO; tun = priv->tun; @@ -736,8 +737,8 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, config |= TDA9887_PORT2_ACTIVE; if (t_params->intercarrier_mode) config |= TDA9887_INTERCARRIER; -/* if (t_params->port1_set_for_fm_mono) - config &= ~TDA9887_PORT1_ACTIVE;*/ + if (t_params->port1_set_for_fm_mono && mono) + config &= ~TDA9887_PORT1_ACTIVE; if (t_params->fm_gain_normal) config |= TDA9887_GAIN_NORMAL; if (t_params->radio_if == 2) -- cgit v1.2.3-70-g09d2 From 2473394343c338426689476aec42790e6d40731c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 10:05:56 -0400 Subject: media: platform: exynos4-is: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 6 ++---- drivers/media/platform/exynos4-is/mipi-csis.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 78b48a1fa26c..deb499f76412 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1201,8 +1201,7 @@ static const struct media_device_ops fimc_md_ops = { static ssize_t fimc_md_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct fimc_md *fmd = platform_get_drvdata(pdev); + struct fimc_md *fmd = dev_get_drvdata(dev); if (fmd->user_subdev_api) return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); @@ -1214,8 +1213,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct platform_device *pdev = to_platform_device(dev); - struct fimc_md *fmd = platform_get_drvdata(pdev); + struct fimc_md *fmd = dev_get_drvdata(dev); bool subdev_api; int i; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index cba46a656338..b4e28a299e26 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -891,8 +891,7 @@ e_clkput: static int s5pcsis_pm_suspend(struct device *dev, bool runtime) { - struct platform_device *pdev = to_platform_device(dev); - struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csis_state *state = sd_to_csis_state(sd); int ret = 0; @@ -921,8 +920,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) static int s5pcsis_pm_resume(struct device *dev, bool runtime) { - struct platform_device *pdev = to_platform_device(dev); - struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csis_state *state = sd_to_csis_state(sd); int ret = 0; -- cgit v1.2.3-70-g09d2 From 666e994aa2278e948e2492ee9d81b4df241e7222 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 10:05:57 -0400 Subject: media: platform: s5p-mfc: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index a80251ed3143..9ca707cb2a42 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1449,8 +1449,7 @@ static int s5p_mfc_remove(struct platform_device *pdev) static int s5p_mfc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); + struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); int ret; if (m_dev->num_inst == 0) @@ -1484,8 +1483,7 @@ static int s5p_mfc_suspend(struct device *dev) static int s5p_mfc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); + struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); if (m_dev->num_inst == 0) return 0; -- cgit v1.2.3-70-g09d2 From 7c1b9a5aeed91bef98988ac0fcf38c8c1f4f9a3a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 15 May 2018 05:21:45 -0400 Subject: media: exynos4-is: Prevent NULL pointer dereference in __isp_video_try_fmt() This patch fixes potential NULL pointer dereference as indicated by the following static checker warning: drivers/media/platform/exynos4-is/fimc-isp-video.c:408 isp_video_try_fmt_mplane() error: NULL dereference inside function '__isp_video_try_fmt(isp, &f->fmt.pix_mp, (0))()'. Fixes: 34947b8aebe3: ("[media] exynos4-is: Add the FIMC-IS ISP capture DMA driver") Reported-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 55ba696b8cf4..a920164f53f1 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -384,12 +384,17 @@ static void __isp_video_try_fmt(struct fimc_isp *isp, struct v4l2_pix_format_mplane *pixm, const struct fimc_fmt **fmt) { - *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); + const struct fimc_fmt *__fmt; + + __fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); + + if (fmt) + *fmt = __fmt; pixm->colorspace = V4L2_COLORSPACE_SRGB; pixm->field = V4L2_FIELD_NONE; - pixm->num_planes = (*fmt)->memplanes; - pixm->pixelformat = (*fmt)->fourcc; + pixm->num_planes = __fmt->memplanes; + pixm->pixelformat = __fmt->fourcc; /* * TODO: double check with the docmentation these width/height * constraints are correct. -- cgit v1.2.3-70-g09d2 From 39fbb88165b2bbbc77ea7acab5f10632a31526e6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 4 Jul 2018 10:57:58 -0400 Subject: media: bpf: ensure bpf program is freed on detach Currently we are leaking bpf programs when they are detached from the lirc device; the refcount never reaches zero. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/bpf-lirc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c index 40826bba06b6..55400317ec53 100644 --- a/drivers/media/rc/bpf-lirc.c +++ b/drivers/media/rc/bpf-lirc.c @@ -174,6 +174,7 @@ static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog) rcu_assign_pointer(raw->progs, new_array); bpf_prog_array_free(old_array); + bpf_prog_put(prog); unlock: mutex_unlock(&ir_raw_handler_lock); return ret; -- cgit v1.2.3-70-g09d2 From b5f6ec535f8f7ca3516adc0f34f2c3a5a388ad58 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 3 Jul 2018 09:54:39 -0400 Subject: media: smiapp: Set correct MODULE_LICENSE The smiapp driver is licensed under GNU GPL v2 only, as stated by the header. Reflect this in the MODULE_LICENSE macro. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index e9e0f21efc2a..f2daad49ff18 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -3187,4 +3187,4 @@ module_i2c_driver(smiapp_i2c_driver); MODULE_AUTHOR("Sakari Ailus "); MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 6949d864776ef295ddc8f840d7d6a2dbe51bd9d1 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Wed, 4 Jul 2018 09:04:38 -0400 Subject: media: ov5640: do not change mode if format or frame interval is unchanged Save load of mode registers array when V4L2 client sets a format or a frame interval which selects the same mode than the current one. Signed-off-by: Hugues Fruchet Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 1ecbb7a4a2ee..071f4bc240ca 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1966,9 +1966,11 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, goto out; } - sensor->current_mode = new_mode; - sensor->fmt = *mbus_fmt; - sensor->pending_mode_change = true; + if (new_mode != sensor->current_mode) { + sensor->current_mode = new_mode; + sensor->fmt = *mbus_fmt; + sensor->pending_mode_change = true; + } out: mutex_unlock(&sensor->lock); return ret; @@ -2508,8 +2510,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, goto out; } - sensor->current_mode = mode; - sensor->pending_mode_change = true; + if (mode != sensor->current_mode) { + sensor->current_mode = mode; + sensor->pending_mode_change = true; + } out: mutex_unlock(&sensor->lock); return ret; -- cgit v1.2.3-70-g09d2 From 18d6a9b831985c7c646abd751509f52079420104 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 5 Jul 2018 10:41:11 -0400 Subject: media: smiapp: fix debug message ask_h gets printed here instead of ask_w. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index f2daad49ff18..1236683da8f7 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1892,7 +1892,7 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, val -= SCALING_GOODNESS_EXTREME; dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", - w, ask_h, h, ask_h, val); + w, ask_w, h, ask_h, val); return val; } -- cgit v1.2.3-70-g09d2 From 9b04fcc1d8a9b5480f9e7ec93072c3a1bc517256 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 9 Jul 2018 10:19:19 -0400 Subject: media: v4l2-fwnode: parse 'data-enable-active' prop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse the newly defined 'data-enable-active' property in parallel endpoint parsing function. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 4 ++++ include/media/v4l2-mediabus.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 3f77aa318035..61051911e779 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -154,6 +154,10 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus( flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) + flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH : + V4L2_MBUS_DATA_ENABLE_LOW; + bus->flags = flags; } diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 4d8626c468bc..4bbb5f3d2b02 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -45,6 +45,8 @@ /* Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. */ #define V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH BIT(12) #define V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW BIT(13) +#define V4L2_MBUS_DATA_ENABLE_HIGH BIT(14) +#define V4L2_MBUS_DATA_ENABLE_LOW BIT(15) /* Serial flags */ /* How many lanes the client can use */ -- cgit v1.2.3-70-g09d2 From 53cf3100dd3af5f4ffbbe272e34f2150157aa425 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 9 Jul 2018 10:19:21 -0400 Subject: media: rcar-vin: Handle data-enable polarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle data-enable signal polarity. If the polarity is not specifically requested to be active low, use the active high default. Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 1de598120286..9146f63430c2 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -123,6 +123,7 @@ /* Video n Data Mode Register 2 bits */ #define VNDMR2_VPS (1 << 30) #define VNDMR2_HPS (1 << 29) +#define VNDMR2_CES (1 << 28) #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) @@ -698,6 +699,10 @@ static int rvin_setup(struct rvin_dev *vin) /* Vsync Signal Polarity Select */ if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_VPS; + + /* Data Enable Polarity Select */ + if (vin->parallel->mbus_flags & V4L2_MBUS_DATA_ENABLE_LOW) + dmr2 |= VNDMR2_CES; } /* -- cgit v1.2.3-70-g09d2 From 4adb0a0432f489c5eb802b33dae7737f69e6fd7a Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Mon, 18 Jun 2018 04:06:58 -0400 Subject: media: ov5645: Supported external clock is 24MHz The external clock frequency was set to 23.88MHz by mistake because of a platform which cannot get closer to 24MHz. The supported by the driver external clock is 24MHz so set it correctly and also fix the values of the pixel clock and link clock. However allow 1% tolerance to the external clock as this difference is small enough to be insignificant. Signed-off-by: Todor Tomov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5645.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index b3f762578f7f..1722cdab0daf 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -510,8 +510,8 @@ static const struct reg_value ov5645_setting_full[] = { }; static const s64 link_freq[] = { - 222880000, - 334320000 + 224000000, + 336000000 }; static const struct ov5645_mode_info ov5645_mode_info_data[] = { @@ -520,7 +520,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 960, .data = ov5645_setting_sxga, .data_size = ARRAY_SIZE(ov5645_setting_sxga), - .pixel_clock = 111440000, + .pixel_clock = 112000000, .link_freq = 0 /* an index in link_freq[] */ }, { @@ -528,7 +528,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 1080, .data = ov5645_setting_1080p, .data_size = ARRAY_SIZE(ov5645_setting_1080p), - .pixel_clock = 167160000, + .pixel_clock = 168000000, .link_freq = 1 /* an index in link_freq[] */ }, { @@ -536,7 +536,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 1944, .data = ov5645_setting_full, .data_size = ARRAY_SIZE(ov5645_setting_full), - .pixel_clock = 167160000, + .pixel_clock = 168000000, .link_freq = 1 /* an index in link_freq[] */ }, }; @@ -1145,7 +1145,8 @@ static int ov5645_probe(struct i2c_client *client, return ret; } - if (xclk_freq != 23880000) { + /* external clock must be 24MHz, allow 1% tolerance */ + if (xclk_freq < 23760000 || xclk_freq > 24240000) { dev_err(dev, "external clock frequency %u is not supported\n", xclk_freq); return -EINVAL; -- cgit v1.2.3-70-g09d2 From 09a48f74e4e379af07aef7e2eb48a9be482aa8ff Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 31 May 2018 13:35:03 -0400 Subject: media: i2c: ov7670: Put ep fwnode after use The just parsed endpoint fwnode has to be put after use. Currently this is done only in error handling path. Fix that by putting node unconditionally after use. Fixes: 01b8444828fc ("media: v4l2: i2c: ov7670: Implement OF mbus configuration") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 3474ef832c1e..31bf577b0bd3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1744,14 +1744,12 @@ static int ov7670_parse_dt(struct device *dev, return -EINVAL; ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg); - if (ret) { - fwnode_handle_put(ep); + fwnode_handle_put(ep); + if (ret) return ret; - } if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) { dev_err(dev, "Unsupported media bus type\n"); - fwnode_handle_put(ep); return ret; } info->mbus_config = bus_cfg.bus.parallel.flags; -- cgit v1.2.3-70-g09d2 From be9543ec9e7423894473ec9314023e505e26fc52 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 16 Jul 2018 11:20:33 -0400 Subject: media: v4l: i2c: Replace "sensor-level" by "sensor" A lot of sensor drivers are labelled as "sensor-level" drivers. That's odd and somewhat confusing as the term isn't used elsewhere: these are just sensor drivers. Call them such. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 62 +++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8669853e1361..5ee7a09001a7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -398,7 +398,7 @@ config VIDEO_TVP514X depends on VIDEO_V4L2 && I2C select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + This is a Video4Linux2 sensor driver for the TI TVP5146/47 decoder. It is currently working with the TI OMAP3 camera controller. @@ -600,7 +600,7 @@ config VIDEO_IMX258 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Sony + This is a Video4Linux2 sensor driver for the Sony IMX258 camera. To compile this driver as a module, choose M here: the @@ -611,7 +611,7 @@ config VIDEO_IMX274 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a V4L2 sensor-level driver for the Sony IMX274 + This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. config VIDEO_OV2640 @@ -619,7 +619,7 @@ config VIDEO_OV2640 depends on VIDEO_V4L2 && I2C depends on MEDIA_CAMERA_SUPPORT help - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. To compile this driver as a module, choose M here: the @@ -631,7 +631,7 @@ config VIDEO_OV2659 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2659 camera. To compile this driver as a module, choose M here: the @@ -643,7 +643,7 @@ config VIDEO_OV2685 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2685 camera. To compile this driver as a module, choose M here: the @@ -656,7 +656,7 @@ config VIDEO_OV5640 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the Omnivision + This is a Video4Linux2 sensor driver for the Omnivision OV5640 camera sensor with a MIPI CSI-2 interface. config VIDEO_OV5645 @@ -666,7 +666,7 @@ config VIDEO_OV5645 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5645 camera. To compile this driver as a module, choose M here: the @@ -678,7 +678,7 @@ config VIDEO_OV5647 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5647 camera. To compile this driver as a module, choose M here: the @@ -689,7 +689,7 @@ config VIDEO_OV6650 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. To compile this driver as a module, choose M here: the @@ -702,7 +702,7 @@ config VIDEO_OV5670 depends on MEDIA_CONTROLLER select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5670 camera. To compile this driver as a module, choose M here: the @@ -713,7 +713,7 @@ config VIDEO_OV5695 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. To compile this driver as a module, choose M here: the @@ -725,7 +725,7 @@ config VIDEO_OV7251 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7251 camera. To compile this driver as a module, choose M here: the @@ -736,7 +736,7 @@ config VIDEO_OV772X depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. To compile this driver as a module, choose M here: the @@ -747,7 +747,7 @@ config VIDEO_OV7640 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. To compile this driver as a module, choose M here: the @@ -759,7 +759,7 @@ config VIDEO_OV7670 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 controller. @@ -768,14 +768,14 @@ config VIDEO_OV7740 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for the Omnivision + This is a V4L2 sensor driver for the Omnivision OV9650 and OV9652 camera sensors. config VIDEO_OV13858 @@ -784,7 +784,7 @@ config VIDEO_OV13858 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV13858 camera. config VIDEO_VS6624 @@ -792,7 +792,7 @@ config VIDEO_VS6624 depends on VIDEO_V4L2 && I2C depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the ST VS6624 + This is a Video4Linux2 sensor driver for the ST VS6624 camera. To compile this driver as a module, choose M here: the @@ -820,7 +820,7 @@ config VIDEO_MT9P031 depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) mt9p031 5 Mpixel camera. config VIDEO_MT9T001 @@ -828,7 +828,7 @@ config VIDEO_MT9T001 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) mt0t001 3 Mpixel camera. config VIDEO_MT9T112 @@ -836,7 +836,7 @@ config VIDEO_MT9T112 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. To compile this driver as a module, choose M here: the @@ -847,7 +847,7 @@ config VIDEO_MT9V011 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Micron + This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the em28xx driver. @@ -858,7 +858,7 @@ config VIDEO_MT9V032 select REGMAP_I2C select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the Micron + This is a Video4Linux2 sensor driver for the Micron MT9V032 752x480 CMOS sensor. config VIDEO_SR030PC30 @@ -882,7 +882,7 @@ config VIDEO_RJ54N1 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT help - This is a V4L2 sensor-level driver for Sharp RJ54N1CB0C CMOS image + This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. To compile this driver as a module, choose M here: the @@ -893,7 +893,7 @@ config VIDEO_S5K6AA depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K6A3 @@ -901,7 +901,7 @@ config VIDEO_S5K6A3 depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6A3 raw + This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. config VIDEO_S5K4ECGX @@ -909,7 +909,7 @@ config VIDEO_S5K4ECGX depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select CRC32 ---help--- - This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M + This is a V4L2 sensor driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K5BAF @@ -917,7 +917,7 @@ config VIDEO_S5K5BAF depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE ---help--- - This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M + This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. source "drivers/media/i2c/smiapp/Kconfig" @@ -928,7 +928,7 @@ config VIDEO_S5C73M3 depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE ---help--- - This is a V4L2 sensor-level driver for Samsung S5C73M3 + This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. comment "Flash devices" -- cgit v1.2.3-70-g09d2 From 56ab8cdbc1438507d79085fcc7e511327d84aeb8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 17 May 2018 10:30:16 -0400 Subject: media: v4l: Add support for STD ioctls on subdev nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no way to control the standard of subdevices which are part of a media device. The ioctls which exists all target video devices explicitly and the idea is that the video device should talk to the subdevice. For subdevices part of a media graph this is not possible and the standard must be controlled on the subdev device directly. Add four new ioctls to be able to directly interact with subdevices and control the video standard; VIDIOC_SUBDEV_ENUMSTD, VIDIOC_SUBDEV_G_STD, VIDIOC_SUBDEV_S_STD and VIDIOC_SUBDEV_QUERYSTD. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/vidioc-enumstd.rst | 11 +++++++---- Documentation/media/uapi/v4l/vidioc-g-std.rst | 14 ++++++++++---- Documentation/media/uapi/v4l/vidioc-querystd.rst | 11 +++++++---- drivers/media/v4l2-core/v4l2-subdev.c | 22 ++++++++++++++++++++++ include/uapi/linux/v4l2-subdev.h | 4 ++++ 5 files changed, 50 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/vidioc-enumstd.rst b/Documentation/media/uapi/v4l/vidioc-enumstd.rst index b7fda29f46a1..2644a62acd4b 100644 --- a/Documentation/media/uapi/v4l/vidioc-enumstd.rst +++ b/Documentation/media/uapi/v4l/vidioc-enumstd.rst @@ -2,14 +2,14 @@ .. _VIDIOC_ENUMSTD: -******************** -ioctl VIDIOC_ENUMSTD -******************** +******************************************* +ioctl VIDIOC_ENUMSTD, VIDIOC_SUBDEV_ENUMSTD +******************************************* Name ==== -VIDIOC_ENUMSTD - Enumerate supported video standards +VIDIOC_ENUMSTD - VIDIOC_SUBDEV_ENUMSTD - Enumerate supported video standards Synopsis @@ -18,6 +18,9 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_ENUMSTD, struct v4l2_standard *argp ) :name: VIDIOC_ENUMSTD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_ENUMSTD, struct v4l2_standard *argp ) + :name: VIDIOC_SUBDEV_ENUMSTD + Arguments ========= diff --git a/Documentation/media/uapi/v4l/vidioc-g-std.rst b/Documentation/media/uapi/v4l/vidioc-g-std.rst index 90791ab51a53..8d94f0404df2 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-std.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-std.rst @@ -2,14 +2,14 @@ .. _VIDIOC_G_STD: -******************************** -ioctl VIDIOC_G_STD, VIDIOC_S_STD -******************************** +************************************************************************** +ioctl VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_SUBDEV_G_STD, VIDIOC_SUBDEV_S_STD +************************************************************************** Name ==== -VIDIOC_G_STD - VIDIOC_S_STD - Query or select the video standard of the current input +VIDIOC_G_STD - VIDIOC_S_STD - VIDIOC_SUBDEV_G_STD - VIDIOC_SUBDEV_S_STD - Query or select the video standard of the current input Synopsis @@ -21,6 +21,12 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_S_STD, const v4l2_std_id *argp ) :name: VIDIOC_S_STD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_G_STD, v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_G_STD + +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_S_STD, const v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_S_STD + Arguments ========= diff --git a/Documentation/media/uapi/v4l/vidioc-querystd.rst b/Documentation/media/uapi/v4l/vidioc-querystd.rst index cf40bca19b9f..a8385cc74818 100644 --- a/Documentation/media/uapi/v4l/vidioc-querystd.rst +++ b/Documentation/media/uapi/v4l/vidioc-querystd.rst @@ -2,14 +2,14 @@ .. _VIDIOC_QUERYSTD: -********************* -ioctl VIDIOC_QUERYSTD -********************* +********************************************* +ioctl VIDIOC_QUERYSTD, VIDIOC_SUBDEV_QUERYSTD +********************************************* Name ==== -VIDIOC_QUERYSTD - Sense the video standard received by the current input +VIDIOC_QUERYSTD - VIDIOC_SUBDEV_QUERYSTD - Sense the video standard received by the current input Synopsis @@ -18,6 +18,9 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_QUERYSTD, v4l2_std_id *argp ) :name: VIDIOC_QUERYSTD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_QUERYSTD, v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_QUERYSTD + Arguments ========= diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6a7f7f75dfd7..2b63fa6b6fc9 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -494,6 +494,28 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_DV_TIMINGS: return v4l2_subdev_call(sd, video, s_dv_timings, arg); + + case VIDIOC_SUBDEV_G_STD: + return v4l2_subdev_call(sd, video, g_std, arg); + + case VIDIOC_SUBDEV_S_STD: { + v4l2_std_id *std = arg; + + return v4l2_subdev_call(sd, video, s_std, *std); + } + + case VIDIOC_SUBDEV_ENUMSTD: { + struct v4l2_standard *p = arg; + v4l2_std_id id; + + if (v4l2_subdev_call(sd, video, g_tvnorms, &id)) + return -EINVAL; + + return v4l_video_std_enumstd(p, id); + } + + case VIDIOC_SUBDEV_QUERYSTD: + return v4l2_subdev_call(sd, video, querystd, arg); #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index c95a53e6743c..03970ce30741 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -170,8 +170,12 @@ struct v4l2_subdev_selection { #define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) /* The following ioctls are identical to the ioctls in videodev2.h */ +#define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) +#define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) +#define VIDIOC_SUBDEV_ENUMSTD _IOWR('V', 25, struct v4l2_standard) #define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid) #define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid) +#define VIDIOC_SUBDEV_QUERYSTD _IOR('V', 63, v4l2_std_id) #define VIDIOC_SUBDEV_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_SUBDEV_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) #define VIDIOC_SUBDEV_ENUM_DV_TIMINGS _IOWR('V', 98, struct v4l2_enum_dv_timings) -- cgit v1.2.3-70-g09d2 From efe1958ec41baba26e47a271b992b2bb524af61e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 24 May 2018 10:50:44 -0400 Subject: media: video-mux: fix compliance failures Limit frame sizes to the [1, 65536] interval, media bus formats to the available list of formats, and initialize pad and try formats. Reported-by: Rui Miguel Silva Signed-off-by: Philipp Zabel Tested-by: Rui Miguel Silva Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/video-mux.c | 119 +++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 1fb887293337..c01e1592ad0a 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -34,6 +34,13 @@ struct video_mux { int active; }; +static const struct v4l2_mbus_framefmt video_mux_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd) { return container_of(sd, struct video_mux, subdev); @@ -180,6 +187,88 @@ static int video_mux_set_format(struct v4l2_subdev *sd, if (!source_mbusformat) return -EINVAL; + /* No size limitations except V4L2 compliance requirements */ + v4l_bound_align_image(&sdformat->format.width, 1, 65536, 0, + &sdformat->format.height, 1, 65536, 0, 0); + + /* All formats except LVDS and vendor specific formats are acceptable */ + switch (sdformat->format.code) { + case MEDIA_BUS_FMT_RGB444_1X12: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_BGR565_2X8_BE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_GBR888_1X24: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_ARGB8888_1X32: + case MEDIA_BUS_FMT_RGB888_1X32_PADHI: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_UV8_1X8: + case MEDIA_BUS_FMT_UYVY8_1_5X8: + case MEDIA_BUS_FMT_VYUY8_1_5X8: + case MEDIA_BUS_FMT_YUYV8_1_5X8: + case MEDIA_BUS_FMT_YVYU8_1_5X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_VYUY10_2X10: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_YVYU10_2X10: + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_VYUY12_2X12: + case MEDIA_BUS_FMT_YUYV12_2X12: + case MEDIA_BUS_FMT_YVYU12_2X12: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YDYUYDYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_AYUV8_1X32: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + case MEDIA_BUS_FMT_JPEG_1X8: + case MEDIA_BUS_FMT_AHSV8888_1X32: + break; + default: + sdformat->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + if (sdformat->format.field == V4L2_FIELD_ANY) + sdformat->format.field = V4L2_FIELD_NONE; + mutex_lock(&vmux->lock); /* Source pad mirrors active sink pad, no limitations on sink pads */ @@ -197,7 +286,27 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } +static int video_mux_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + struct v4l2_mbus_framefmt *mbusformat; + unsigned int i; + + mutex_lock(&vmux->lock); + + for (i = 0; i < sd->entity.num_pads; i++) { + mbusformat = v4l2_subdev_get_try_format(sd, cfg, i); + *mbusformat = video_mux_format_mbus_default; + } + + mutex_unlock(&vmux->lock); + + return 0; +} + static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { + .init_cfg = video_mux_init_cfg, .get_fmt = video_mux_get_format, .set_fmt = video_mux_set_format, }; @@ -214,8 +323,8 @@ static int video_mux_probe(struct platform_device *pdev) struct device_node *ep; struct video_mux *vmux; unsigned int num_pads = 0; + unsigned int i; int ret; - int i; vmux = devm_kzalloc(dev, sizeof(*vmux), GFP_KERNEL); if (!vmux) @@ -260,9 +369,11 @@ static int video_mux_probe(struct platform_device *pdev) sizeof(*vmux->format_mbus), GFP_KERNEL); - for (i = 0; i < num_pads - 1; i++) - vmux->pads[i].flags = MEDIA_PAD_FL_SINK; - vmux->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; + for (i = 0; i < num_pads; i++) { + vmux->pads[i].flags = (i < num_pads - 1) ? MEDIA_PAD_FL_SINK + : MEDIA_PAD_FL_SOURCE; + vmux->format_mbus[i] = video_mux_format_mbus_default; + } vmux->subdev.entity.function = MEDIA_ENT_F_VID_MUX; ret = media_entity_pads_init(&vmux->subdev.entity, num_pads, -- cgit v1.2.3-70-g09d2 From 7a0167fb68e3c9ec2386f8abb326a4643ee8ef42 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jun 2018 00:38:50 -0400 Subject: media: rcar_jpu: Remove unrequired wait in .job_abort As per the documentation, job_abort is not required to wait until the current job finishes. It is redundant to do so, as the core will perform the wait operation. Remove the wait infrastructure completely. Signed-off-by: Ezequiel Garcia Acked-by: Mikhail Ulyanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_jpu.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 690631747c5e..90d40b2d6259 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -198,7 +198,6 @@ * @vfd_decoder: video device node for decoder mem2mem mode * @m2m_dev: v4l2 mem2mem device data * @curr: pointer to current context - * @irq_queue: interrupt handler waitqueue * @regs: JPEG IP registers mapping * @irq: JPEG IP irq * @clk: JPEG IP clock @@ -213,7 +212,6 @@ struct jpu { struct video_device vfd_decoder; struct v4l2_m2m_dev *m2m_dev; struct jpu_ctx *curr; - wait_queue_head_t irq_queue; void __iomem *regs; unsigned int irq; @@ -1496,11 +1494,6 @@ static void jpu_device_run(void *priv) static void jpu_job_abort(void *priv) { - struct jpu_ctx *ctx = priv; - - if (!wait_event_timeout(ctx->jpu->irq_queue, !ctx->jpu->curr, - msecs_to_jiffies(JPU_JOB_TIMEOUT))) - jpu_cleanup(ctx, true); } static const struct v4l2_m2m_ops jpu_m2m_ops = { @@ -1586,9 +1579,6 @@ static irqreturn_t jpu_irq_handler(int irq, void *dev_id) v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx); - /* ...wakeup abort routine if needed */ - wake_up(&jpu->irq_queue); - return IRQ_HANDLED; handled: @@ -1622,7 +1612,6 @@ static int jpu_probe(struct platform_device *pdev) if (!jpu) return -ENOMEM; - init_waitqueue_head(&jpu->irq_queue); mutex_init(&jpu->mutex); spin_lock_init(&jpu->lock); jpu->dev = &pdev->dev; -- cgit v1.2.3-70-g09d2 From 774f1c912776330ece84540fac370ec201c878b4 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jun 2018 00:38:51 -0400 Subject: media: s5p-g2d: Remove unrequired wait in .job_abort As per the documentation, job_abort is not required to wait until the current job finishes. It is redundant to do so, as the core will perform the wait operation. Remove the wait infrastructure completely. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 11 ----------- drivers/media/platform/s5p-g2d/g2d.h | 1 - 2 files changed, 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 6b1c23463009..ee7322b33666 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -485,15 +485,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c static void job_abort(void *prv) { - struct g2d_ctx *ctx = prv; - struct g2d_dev *dev = ctx->dev; - - if (dev->curr == NULL) /* No job currently running */ - return; - - wait_event_timeout(dev->irq_queue, - dev->curr == NULL, - msecs_to_jiffies(G2D_TIMEOUT)); } static void device_run(void *prv) @@ -565,7 +556,6 @@ static irqreturn_t g2d_isr(int irq, void *prv) v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); dev->curr = NULL; - wake_up(&dev->irq_queue); return IRQ_HANDLED; } @@ -635,7 +625,6 @@ static int g2d_probe(struct platform_device *pdev) spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); - init_waitqueue_head(&dev->irq_queue); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index dd812b557e87..9ffb458a1b93 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -31,7 +31,6 @@ struct g2d_dev { struct g2d_ctx *curr; struct g2d_variant *variant; int irq; - wait_queue_head_t irq_queue; }; struct g2d_frame { -- cgit v1.2.3-70-g09d2 From 5525b8314389a0c558d15464e86f438974b94e32 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jun 2018 00:38:52 -0400 Subject: media: mem2mem: Make .job_abort optional Implementing job_abort() does not make sense on some drivers. This is not a problem, as the abort is not required to wait for the job to finish. Quite the opposite, drivers are encouraged not to wait. Demote v4l2_m2m_ops.job_abort from required to optional, and clean all drivers with dummy implementations. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 5 ----- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 5 ----- drivers/media/platform/rcar_jpu.c | 5 ----- drivers/media/platform/rockchip/rga/rga.c | 6 ------ drivers/media/platform/s5p-g2d/g2d.c | 5 ----- drivers/media/platform/s5p-jpeg/jpeg-core.c | 7 ------- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- include/media/v4l2-mem2mem.h | 2 +- 8 files changed, 4 insertions(+), 37 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 328e8f650d9b..4f24da8afecc 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -861,14 +861,9 @@ static int mtk_jpeg_job_ready(void *priv) return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0; } -static void mtk_jpeg_job_abort(void *priv) -{ -} - static const struct v4l2_m2m_ops mtk_jpeg_m2m_ops = { .device_run = mtk_jpeg_device_run, .job_ready = mtk_jpeg_job_ready, - .job_abort = mtk_jpeg_job_abort, }; static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq, diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index c2e2f5f1ebf1..ceffc31cc6eb 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -441,10 +441,6 @@ static void mtk_mdp_m2m_stop_streaming(struct vb2_queue *q) pm_runtime_put(&ctx->mdp_dev->pdev->dev); } -static void mtk_mdp_m2m_job_abort(void *priv) -{ -} - /* The color format (num_planes) must be already configured. */ static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx, struct vb2_buffer *vb, @@ -1215,7 +1211,6 @@ static const struct v4l2_file_operations mtk_mdp_m2m_fops = { static const struct v4l2_m2m_ops mtk_mdp_m2m_ops = { .device_run = mtk_mdp_m2m_device_run, - .job_abort = mtk_mdp_m2m_job_abort, }; int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp) diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 90d40b2d6259..a808258a4180 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1492,13 +1492,8 @@ static void jpu_device_run(void *priv) spin_unlock_irqrestore(&ctx->jpu->lock, flags); } -static void jpu_job_abort(void *priv) -{ -} - static const struct v4l2_m2m_ops jpu_m2m_ops = { .device_run = jpu_device_run, - .job_abort = jpu_job_abort, }; /* diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 8ace1873202a..69a2797d7bbe 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -39,11 +39,6 @@ static int debug; module_param(debug, int, 0644); -static void job_abort(void *prv) -{ - /* Can't do anything rational here */ -} - static void device_run(void *prv) { struct rga_ctx *ctx = prv; @@ -104,7 +99,6 @@ static irqreturn_t rga_isr(int irq, void *prv) static struct v4l2_m2m_ops rga_m2m_ops = { .device_run = device_run, - .job_abort = job_abort, }; static int diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index ee7322b33666..e901201b6fcc 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -483,10 +483,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c return 0; } -static void job_abort(void *prv) -{ -} - static void device_run(void *prv) { struct g2d_ctx *ctx = prv; @@ -605,7 +601,6 @@ static const struct video_device g2d_videodev = { static const struct v4l2_m2m_ops g2d_m2m_ops = { .device_run = device_run, - .job_abort = job_abort, }; static const struct of_device_id exynos_g2d_match[]; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 79b63da27f53..04fd2e0493c0 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2467,26 +2467,19 @@ static int s5p_jpeg_job_ready(void *priv) return 1; } -static void s5p_jpeg_job_abort(void *priv) -{ -} - static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { .device_run = s5p_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; static struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { .device_run = exynos3250_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { .device_run = exynos4_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; /* diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 725da74d15d8..4aeedb91594a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -322,7 +322,8 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) m2m_ctx->job_flags |= TRANS_ABORT; if (m2m_ctx->job_flags & TRANS_RUNNING) { spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + if (m2m_dev->m2m_ops->job_abort) + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); @@ -788,8 +789,7 @@ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; - if (!m2m_ops || WARN_ON(!m2m_ops->device_run) || - WARN_ON(!m2m_ops->job_abort)) + if (!m2m_ops || WARN_ON(!m2m_ops->device_run)) return ERR_PTR(-EINVAL); m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index af48b1eca025..d60d3060f762 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -32,7 +32,7 @@ * assumed that one source and one destination buffer are all * that is required for the driver to perform one full transaction. * This method may not sleep. - * @job_abort: required. Informs the driver that it has to abort the currently + * @job_abort: optional. Informs the driver that it has to abort the currently * running transaction as soon as possible (i.e. as soon as it can * stop the device safely; e.g. in the next interrupt handler), * even if the transaction would not have been finished by then. -- cgit v1.2.3-70-g09d2 From b509d733d337417bcb7fa4a35be3b9a49332b724 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Jul 2018 04:25:19 -0400 Subject: media: videobuf2-core: check for q->error in vb2_core_qbuf() The vb2_core_qbuf() function didn't check if q->error was set. It is checked in __buf_prepare(), but that function isn't called if the buffer was already prepared before with VIDIOC_PREPARE_BUF. So check it at the start of vb2_core_qbuf() as well. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index f32ec7342ef0..5653e8eebe2b 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1377,6 +1377,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb) struct vb2_buffer *vb; int ret; + if (q->error) { + dprintk(1, "fatal error occurred on queue\n"); + return -EIO; + } + vb = q->bufs[index]; switch (vb->state) { -- cgit v1.2.3-70-g09d2 From 6479aa888222b344dfb087303e2f9e8d8394ef13 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 10 Jul 2018 09:44:57 -0400 Subject: media: platform: meson-ao-cec: make busy TX warning silent Switch to dev_dbg for the busy TX message to avoid having a flood of: [ 228.064570] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting [ 230.368489] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting [ 234.208655] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting [ 236.512558] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting This message is only a debug hint and not an error. Fixes: 7ec2c0f72cb1 ("media: platform: Add Amlogic Meson AO CEC Controller driver") Signed-off-by: Neil Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/meson/ao-cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c index 8040a6285c3f..cd4be38ab5ac 100644 --- a/drivers/media/platform/meson/ao-cec.c +++ b/drivers/media/platform/meson/ao-cec.c @@ -524,7 +524,7 @@ static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts, return ret; if (reg == TX_BUSY) { - dev_err(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", + dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", __func__); meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); } -- cgit v1.2.3-70-g09d2 From 231783073ebfc4acf02a45cb78a52ffa16e4e6d3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 11 Jul 2018 10:23:32 -0400 Subject: media: v4l: rcar_fdp1: Enable compilation on Gen2 platforms Commit 1d3897143815 ("[media] v4l: rcar_fdp1: add FCP dependency") fixed a compilation breakage when the optional VIDEO_RENESAS_FCP dependency is compiled as a module while the rcar_fdp1 driver is built in. As a side effect it disabled compilation on Gen2 by disallowing the valid combination ARCH_RENESAS && !VIDEO_RENESAS_FCP. Fix it by handling the dependency the same way the vsp1 driver did in commit 199946731fa4 ("[media] vsp1: clarify FCP dependency"). Fixes: 1d3897143815 ("[media] v4l: rcar_fdp1: add FCP dependency") Signed-off-by: Laurent Pinchart Signed-off-by: Geert Uytterhoeven 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(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 210b44a457eb..84cb97eccb2a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -385,7 +385,7 @@ config VIDEO_RENESAS_FDP1 tristate "Renesas Fine Display Processor" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_RENESAS || COMPILE_TEST - depends on (!ARCH_RENESAS && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP + depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- -- cgit v1.2.3-70-g09d2 From 4786b0d6f3ca6da8db02b0a58cd2b785cb0b80ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 28 Jun 2018 07:43:46 -0400 Subject: media: cec: add support for 5V signal testing Add support for the new 5V CEC events Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 18 +++++++++++++++++- drivers/media/cec/cec-api.c | 8 ++++++++ include/media/cec-pin.h | 4 ++++ include/media/cec.h | 12 +++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index b7fad0ec5710..030b2602faf0 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -74,7 +74,7 @@ void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { static const u16 max_events[CEC_NUM_EVENTS] = { - 1, 1, 800, 800, 8, 8, + 1, 1, 800, 800, 8, 8, 8, 8 }; struct cec_event_entry *entry; unsigned int ev_idx = new_ev->event - 1; @@ -176,6 +176,22 @@ void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts) } EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event); +/* Notify userspace that the 5V pin changed state at the given time. */ +void cec_queue_pin_5v_event(struct cec_adapter *adap, bool is_high, ktime_t ts) +{ + struct cec_event ev = { + .event = is_high ? CEC_EVENT_PIN_5V_HIGH : + CEC_EVENT_PIN_5V_LOW, + }; + struct cec_fh *fh; + + mutex_lock(&adap->devnode.lock); + list_for_each_entry(fh, &adap->devnode.fhs, list) + cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); + mutex_unlock(&adap->devnode.lock); +} +EXPORT_SYMBOL_GPL(cec_queue_pin_5v_event); + /* * Queue a new message for this filehandle. * diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 10b67fc40318..b6536bbad530 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -579,6 +579,14 @@ static int cec_open(struct inode *inode, struct file *filp) cec_queue_event_fh(fh, &ev, 0); } } + if (adap->pin && adap->pin->ops->read_5v) { + err = adap->pin->ops->read_5v(adap); + if (err >= 0) { + ev.event = err ? CEC_EVENT_PIN_5V_HIGH : + CEC_EVENT_PIN_5V_LOW; + cec_queue_event_fh(fh, &ev, 0); + } + } #endif list_add(&fh->list, &devnode->fhs); diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index ed16c6dde0ba..604e79cb6cbf 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -25,6 +25,9 @@ * @read_hpd: read the HPD pin. Return true if high, false if low or * an error if negative. If NULL or -ENOTTY is returned, * then this is not supported. + * @read_5v: read the 5V pin. Return true if high, false if low or + * an error if negative. If NULL or -ENOTTY is returned, + * then this is not supported. * * These operations are used by the cec pin framework to manipulate * the CEC pin. @@ -38,6 +41,7 @@ struct cec_pin_ops { void (*free)(struct cec_adapter *adap); void (*status)(struct cec_adapter *adap, struct seq_file *file); int (*read_hpd)(struct cec_adapter *adap); + int (*read_5v)(struct cec_adapter *adap); }; /** diff --git a/include/media/cec.h b/include/media/cec.h index 580ab1042898..ff9847f7f99d 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -79,7 +79,7 @@ struct cec_event_entry { }; #define CEC_NUM_CORE_EVENTS 2 -#define CEC_NUM_EVENTS CEC_EVENT_PIN_HPD_HIGH +#define CEC_NUM_EVENTS CEC_EVENT_PIN_5V_HIGH struct cec_fh { struct list_head list; @@ -308,6 +308,16 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, */ void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts); +/** + * cec_queue_pin_5v_event() - queue a pin event with a given timestamp. + * + * @adap: pointer to the cec adapter + * @is_high: when true the 5V pin is high, otherwise it is low + * @ts: the timestamp for this event + * + */ +void cec_queue_pin_5v_event(struct cec_adapter *adap, bool is_high, ktime_t ts); + /** * cec_get_edid_phys_addr() - find and return the physical address * -- cgit v1.2.3-70-g09d2 From 76e464888b1c367fddaf67f23ee3e0aa4ca8cef4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 28 Jun 2018 07:44:03 -0400 Subject: media: cec-gpio: support 5v testing Add support for the new (optional) 5V gpio in order to debug 5V changes. Some displays turn off CEC if the 5V is not detected, so it is useful to be able to monitor this line. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cec-gpio/cec-gpio.c | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index 69f8242209c2..d2861749d640 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -23,6 +23,11 @@ struct cec_gpio { int hpd_irq; bool hpd_is_high; ktime_t hpd_ts; + + struct gpio_desc *v5_gpio; + int v5_irq; + bool v5_is_high; + ktime_t v5_ts; }; static bool cec_gpio_read(struct cec_adapter *adap) @@ -65,6 +70,26 @@ static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) return IRQ_HANDLED; } +static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + bool is_high = gpiod_get_value(cec->v5_gpio); + + if (is_high == cec->v5_is_high) + return IRQ_HANDLED; + cec->v5_ts = ktime_get(); + cec->v5_is_high = is_high; + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + + cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); + return IRQ_HANDLED; +} + static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; @@ -119,6 +144,9 @@ static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) if (cec->hpd_gpio) seq_printf(file, "hpd: %s\n", cec->hpd_is_high ? "high" : "low"); + if (cec->v5_gpio) + seq_printf(file, "5V: %s\n", + cec->v5_is_high ? "high" : "low"); } static int cec_gpio_read_hpd(struct cec_adapter *adap) @@ -130,6 +158,15 @@ static int cec_gpio_read_hpd(struct cec_adapter *adap) return gpiod_get_value(cec->hpd_gpio); } +static int cec_gpio_read_5v(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (!cec->v5_gpio) + return -ENOTTY; + return gpiod_get_value(cec->v5_gpio); +} + static void cec_gpio_free(struct cec_adapter *adap) { cec_gpio_disable_irq(adap); @@ -144,6 +181,7 @@ static const struct cec_pin_ops cec_gpio_pin_ops = { .status = cec_gpio_status, .free = cec_gpio_free, .read_hpd = cec_gpio_read_hpd, + .read_5v = cec_gpio_read_5v, }; static int cec_gpio_probe(struct platform_device *pdev) @@ -167,6 +205,10 @@ static int cec_gpio_probe(struct platform_device *pdev) if (IS_ERR(cec->hpd_gpio)) return PTR_ERR(cec->hpd_gpio); + cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN); + if (IS_ERR(cec->v5_gpio)) + return PTR_ERR(cec->v5_gpio); + cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN); @@ -185,6 +227,18 @@ static int cec_gpio_probe(struct platform_device *pdev) return ret; } + if (cec->v5_gpio) { + cec->v5_irq = gpiod_to_irq(cec->v5_gpio); + ret = devm_request_threaded_irq(dev, cec->v5_irq, + cec_5v_gpio_irq_handler, + cec_5v_gpio_irq_handler_thread, + IRQF_ONESHOT | + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "v5-gpio", cec); + if (ret) + return ret; + } + ret = cec_register_adapter(cec->adap, &pdev->dev); if (ret) { cec_delete_adapter(cec->adap); -- cgit v1.2.3-70-g09d2 From 30b914c8d825da7d4c651ade34667cef05e3ee27 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 27 Feb 2018 07:24:09 -0500 Subject: media: add 'index' to struct media_v2_pad The v2 pad structure never exposed the pad index, which made it impossible to call the MEDIA_IOC_SETUP_LINK ioctl, which needs that information. It is really trivial to just expose this information, so implement this. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 1 + include/uapi/linux/media.h | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 47bb2254fbfd..047d38372a27 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -331,6 +331,7 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) kpad.id = pad->graph_obj.id; kpad.entity_id = pad->entity->graph_obj.id; kpad.flags = pad->flags; + kpad.index = pad->index; if (copy_to_user(upad, &kpad, sizeof(kpad))) ret = -EFAULT; diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 86c7dcc9cba3..f6338bd57929 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -305,11 +305,21 @@ struct media_v2_interface { }; } __attribute__ ((packed)); +/* + * Appeared in 4.19.0. + * + * The media_version argument comes from the media_version field in + * struct media_device_info. + */ +#define MEDIA_V2_PAD_HAS_INDEX(media_version) \ + ((media_version) >= ((4 << 16) | (19 << 8) | 0)) + struct media_v2_pad { __u32 id; __u32 entity_id; __u32 flags; - __u32 reserved[5]; + __u32 index; + __u32 reserved[4]; } __attribute__ ((packed)); struct media_v2_link { -- cgit v1.2.3-70-g09d2 From 588f4ee7e6fc5c9a0fb07c7051cdd341949e0feb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Feb 2018 05:41:11 -0500 Subject: media: add flags field to struct media_v2_entity The v2 entity structure never exposed the entity flags, which made it impossible to detect connector or default entities. It is really trivial to just expose this information, so implement this. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 1 + include/uapi/linux/media.h | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 047d38372a27..14959b19a342 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -266,6 +266,7 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) memset(&kentity, 0, sizeof(kentity)); kentity.id = entity->graph_obj.id; kentity.function = entity->function; + kentity.flags = entity->flags; strlcpy(kentity.name, entity->name, sizeof(kentity.name)); diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index f6338bd57929..ebd2cda67833 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -280,11 +280,21 @@ struct media_links_enum { * MC next gen API definitions */ +/* + * Appeared in 4.19.0. + * + * The media_version argument comes from the media_version field in + * struct media_device_info. + */ +#define MEDIA_V2_ENTITY_HAS_FLAGS(media_version) \ + ((media_version) >= ((4 << 16) | (19 << 8) | 0)) + struct media_v2_entity { __u32 id; char name[64]; __u32 function; /* Main function of the entity */ - __u32 reserved[6]; + __u32 flags; + __u32 reserved[5]; } __attribute__ ((packed)); /* Should match the specific fields at media_intf_devnode */ -- cgit v1.2.3-70-g09d2 From d272bc92c4a4fcec4102c011eaa85630bd2d8d38 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 28 Jun 2018 08:56:02 -0400 Subject: media: rename MEDIA_ENT_F_DTV_DECODER to MEDIA_ENT_F_DV_DECODER The use of 'DTV' is very confusing since it normally refers to Digital TV e.g. DVB etc. Instead use 'DV' (Digital Video), which nicely corresponds to the DV Timings API used to configure such receivers and transmitters. We keep an alias to avoid breaking userspace applications. Since this alias is only available if __KERNEL__ is *not* defined (i.e. it is only available for userspace, not kernelspace), any drivers that use it also have to be converted to the new define. These drivers are adv7604, adv7842 and tda1997x. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/mediactl/media-types.rst | 2 +- drivers/media/i2c/adv7604.c | 1 + drivers/media/i2c/adv7842.c | 1 + drivers/media/i2c/tda1997x.c | 2 +- include/uapi/linux/media.h | 4 +++- 5 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/media/uapi/mediactl/media-types.rst b/Documentation/media/uapi/mediactl/media-types.rst index 96910cf2eaaa..c11b0c7e890b 100644 --- a/Documentation/media/uapi/mediactl/media-types.rst +++ b/Documentation/media/uapi/mediactl/media-types.rst @@ -200,7 +200,7 @@ Types and flags used to represent the media graph elements MIPI CSI-2, etc.), and outputs them on its source pad to an output video bus of another type (eDP, MIPI CSI-2, parallel, etc.). - * - ``MEDIA_ENT_F_DTV_DECODER`` + * - ``MEDIA_ENT_F_DV_DECODER`` - Digital video decoder. The basic function of the video decoder is to accept digital video from a wide variety of sources and output it in some digital video standard, with appropriate diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1a3b2c04d9f9..668be2bca57a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3499,6 +3499,7 @@ static int adv76xx_probe(struct i2c_client *client, for (i = 0; i < state->source_pad; ++i) state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_DV_DECODER; err = media_entity_pads_init(&sd->entity, state->source_pad + 1, state->pads); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index fddac32e5051..99d781343fb1 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3541,6 +3541,7 @@ static int adv7842_probe(struct i2c_client *client, INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv7842_delayed_work_enable_hotplug); + sd->entity.function = MEDIA_ENT_F_DV_DECODER; state->pad.flags = MEDIA_PAD_FL_SOURCE; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 039a92c3294a..d114ac5243ec 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2570,7 +2570,7 @@ static int tda1997x_probe(struct i2c_client *client, id->name, i2c_adapter_id(client->adapter), client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - sd->entity.function = MEDIA_ENT_F_DTV_DECODER; + sd->entity.function = MEDIA_ENT_F_DV_DECODER; sd->entity.ops = &tda1997x_media_ops; /* set allowed mbus modes based on chip, bus-type, and bus-width */ diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index ebd2cda67833..99f5e0978ebb 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -93,7 +93,7 @@ struct media_device_info { * Video decoder functions */ #define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) -#define MEDIA_ENT_F_DTV_DECODER (MEDIA_ENT_F_BASE + 0x6001) +#define MEDIA_ENT_F_DV_DECODER (MEDIA_ENT_F_BASE + 0x6001) /* * Digital TV, analog TV, radio and/or software defined radio tuner functions. @@ -400,6 +400,8 @@ struct media_v2_topology { #define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER #define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER +#define MEDIA_ENT_F_DTV_DECODER MEDIA_ENT_F_DV_DECODER + /* * There is still no ALSA support in the media controller. These * defines should not have been added and we leave them here only -- cgit v1.2.3-70-g09d2 From a20a82b8642145a8d8decc6cd71e68f04647ed23 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Jun 2018 05:08:20 -0400 Subject: media: ad9389b/adv7511: set proper media entity function These two drivers both have function MEDIA_ENT_F_DV_ENCODER. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 1 + drivers/media/i2c/adv7511.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 91ff06088572..5b008b0002c0 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1134,6 +1134,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * goto err_hdl; } state->pad.flags = MEDIA_PAD_FL_SINK; + sd->entity.function = MEDIA_ENT_F_DV_ENCODER; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 5731751d3f2a..55c2ea0720d9 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1847,6 +1847,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * goto err_hdl; } state->pad.flags = MEDIA_PAD_FL_SINK; + sd->entity.function = MEDIA_ENT_F_DV_ENCODER; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; -- cgit v1.2.3-70-g09d2 From ca0fa5f04f33eea010fc1f9dfc87ff58f3a2433f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Jun 2018 05:09:45 -0400 Subject: media: adv7180/tvp514x/tvp7002: fix entity function The entity function was ORed with the flags field instead of assigned to the function field. Correct this. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 2 +- drivers/media/i2c/tvp514x.c | 2 +- drivers/media/i2c/tvp7002.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 25d24a3f10a7..a727d7f806a1 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1335,7 +1335,7 @@ static int adv7180_probe(struct i2c_client *client, goto err_unregister_vpp_client; state->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, 1, &state->pad); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 6a9890531d01..675b9ae212ab 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1084,7 +1084,7 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) decoder->pad.flags = MEDIA_PAD_FL_SOURCE; decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - decoder->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; + decoder->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&decoder->sd.entity, 1, &decoder->pad); if (ret < 0) { diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 4599b7e28a8d..4f5c627579c7 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1010,7 +1010,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) device->pad.flags = MEDIA_PAD_FL_SOURCE; device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - device->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; + device->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); if (error < 0) -- cgit v1.2.3-70-g09d2 From 173bf6e51ba5845fbfcbdd281b837870fb033bc2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Jun 2018 05:10:28 -0400 Subject: media: media/i2c: add missing entity functions Several drivers in media/i2c do not set the entity function. Correct this. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/et8ek8/et8ek8_driver.c | 1 + drivers/media/i2c/mt9m032.c | 1 + drivers/media/i2c/mt9p031.c | 1 + drivers/media/i2c/mt9t001.c | 1 + drivers/media/i2c/mt9v032.c | 1 + 5 files changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index e9eff9039ef5..37ef38947e01 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1446,6 +1446,7 @@ static int et8ek8_probe(struct i2c_client *client, sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->subdev.internal_ops = &et8ek8_internal_ops; + sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) { diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 6a9e068462fd..b385f2b632ad 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -793,6 +793,7 @@ static int mt9m032_probe(struct i2c_client *client, v4l2_ctrl_cluster(2, &sensor->hflip); sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 91d822fc4443..715be3632b01 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -1111,6 +1111,7 @@ static int mt9p031_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; + mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad); if (ret < 0) diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 9d981d9f5686..f683d2cb0486 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -943,6 +943,7 @@ static int mt9t001_probe(struct i2c_client *client, mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9t001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 4de63b2df334..f74730d24d8f 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1162,6 +1162,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9v032->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); if (ret < 0) -- cgit v1.2.3-70-g09d2 From 62c3fce04154777e6a3ce3a27f123b645d36dcff Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 4 Jul 2018 10:13:47 -0400 Subject: media: videodev.h: add PIX_FMT_FWHT for use with vicodec Add a new pixelformat for the vicodec software codec using the Fast Walsh Hadamard Transform. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/pixfmt-compressed.rst | 7 +++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 1 + include/uapi/linux/videodev2.h | 1 + 3 files changed, 9 insertions(+) (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/pixfmt-compressed.rst b/Documentation/media/uapi/v4l/pixfmt-compressed.rst index abec03937bb3..d382e7a5c38e 100644 --- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst +++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst @@ -95,3 +95,10 @@ Compressed Formats - ``V4L2_PIX_FMT_HEVC`` - 'HEVC' - HEVC/H.265 video elementary stream. + * .. _V4L2-PIX-FMT-FWHT: + + - ``V4L2_PIX_FMT_FWHT`` + - 'FWHT' + - Video elementary stream using a codec based on the Fast Walsh Hadamard + Transform. This codec is implemented by the vicodec ('Virtual Codec') + driver. See the vicodec-codec.h header for more details. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 01670567641a..26d9702069fd 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1311,6 +1311,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_VP8: descr = "VP8"; break; case V4L2_PIX_FMT_VP9: descr = "VP9"; break; case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ + case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break; case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 600877be5c22..3ea8097c2470 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -636,6 +636,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */ #define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ #define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */ +#define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ -- cgit v1.2.3-70-g09d2 From ee1228cca15ce097b7badebfdd0fef23a2cca9e1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 26 Jun 2018 09:26:52 -0400 Subject: media: v4l2-mem2mem: add v4l2_m2m_last_buf() This can be used to mark the last queued source buffer as the last buffer. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 18 ++++++++++++++++++ include/media/v4l2-mem2mem.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 4aeedb91594a..d9565ee44578 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -129,6 +129,24 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); +void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) +{ + struct v4l2_m2m_buffer *b; + unsigned long flags; + + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + + if (list_empty(&q_ctx->rdy_queue)) { + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return NULL; + } + + b = list_last_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list); + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return &b->vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_last_buf); + void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index d60d3060f762..d655720e16a1 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -449,6 +449,35 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx); } +/** + * v4l2_m2m_last_buf() - return last buffer from the list of ready buffers + * + * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx + */ +void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); + +/** + * v4l2_m2m_last_src_buf() - return last destination buffer from the list of + * ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +{ + return v4l2_m2m_last_buf(&m2m_ctx->out_q_ctx); +} + +/** + * v4l2_m2m_last_dst_buf() - return last destination buffer from the list of + * ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void *v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +{ + return v4l2_m2m_last_buf(&m2m_ctx->cap_q_ctx); +} + /** * v4l2_m2m_for_each_dst_buf() - iterate over a list of destination ready * buffers -- cgit v1.2.3-70-g09d2 From 251d6fe9a6753f8d2b5b7565c7044dbd82d63d62 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Jul 2018 03:49:21 -0400 Subject: media: vicodec: add the FWHT software codec Add a software codec based on the Fast Walsh Hadamard Transform. The original FWHT codec was developed by Tom aan de Wiel, and it was turned into 'proper' kernel code by Hans Verkuil, with a lot of performance and memory improvements. Signed-off-by: Tom aan de Wiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-codec.c | 797 +++++++++++++++++++++++++ drivers/media/platform/vicodec/vicodec-codec.h | 129 ++++ 2 files changed, 926 insertions(+) create mode 100644 drivers/media/platform/vicodec/vicodec-codec.c create mode 100644 drivers/media/platform/vicodec/vicodec-codec.h (limited to 'drivers/media') diff --git a/drivers/media/platform/vicodec/vicodec-codec.c b/drivers/media/platform/vicodec/vicodec-codec.c new file mode 100644 index 000000000000..e880a6c5747f --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-codec.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2016 Tom aan de Wiel + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * 8x8 Fast Walsh Hadamard Transform in sequency order based on the paper: + * + * A Recursive Algorithm for Sequency-Ordered Fast Walsh Transforms, + * R.D. Brown, 1977 + */ + +#include +#include "vicodec-codec.h" + +#define ALL_ZEROS 15 +#define DEADZONE_WIDTH 20 + +static const uint8_t zigzag[64] = { + 0, + 1, 8, + 2, 9, 16, + 3, 10, 17, 24, + 4, 11, 18, 25, 32, + 5, 12, 19, 26, 33, 40, + 6, 13, 20, 27, 34, 41, 48, + 7, 14, 21, 28, 35, 42, 49, 56, + 15, 22, 29, 36, 43, 50, 57, + 23, 30, 37, 44, 51, 58, + 31, 38, 45, 52, 59, + 39, 46, 53, 60, + 47, 54, 61, + 55, 62, + 63, +}; + + +static int rlc(const s16 *in, __be16 *output, int blocktype) +{ + s16 block[8 * 8]; + s16 *wp = block; + int i = 0; + int x, y; + int ret = 0; + + /* read in block from framebuffer */ + int lastzero_run = 0; + int to_encode; + + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + *wp = in[x + y * 8]; + wp++; + } + } + + /* keep track of amount of trailing zeros */ + for (i = 63; i >= 0 && !block[zigzag[i]]; i--) + lastzero_run++; + + *output++ = (blocktype == PBLOCK ? htons(PFRAME_BIT) : 0); + ret++; + + to_encode = 8 * 8 - (lastzero_run > 14 ? lastzero_run : 0); + + i = 0; + while (i < to_encode) { + int cnt = 0; + int tmp; + + /* count leading zeros */ + while ((tmp = block[zigzag[i]]) == 0 && cnt < 14) { + cnt++; + i++; + if (i == to_encode) { + cnt--; + break; + } + } + /* 4 bits for run, 12 for coefficient (quantization by 4) */ + *output++ = htons((cnt | tmp << 4)); + i++; + ret++; + } + if (lastzero_run > 14) { + *output = htons(ALL_ZEROS | 0); + ret++; + } + + return ret; +} + +/* + * This function will worst-case increase rlc_in by 65*2 bytes: + * one s16 value for the header and 8 * 8 coefficients of type s16. + */ +static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) +{ + /* header */ + const __be16 *input = *rlc_in; + s16 ret = ntohs(*input++); + int dec_count = 0; + s16 block[8 * 8 + 16]; + s16 *wp = block; + int i; + + /* + * Now de-compress, it expands one byte to up to 15 bytes + * (or fills the remainder of the 64 bytes with zeroes if it + * is the last byte to expand). + * + * So block has to be 8 * 8 + 16 bytes, the '+ 16' is to + * allow for overflow if the incoming data was malformed. + */ + while (dec_count < 8 * 8) { + s16 in = ntohs(*input++); + int length = in & 0xf; + int coeff = in >> 4; + + /* fill remainder with zeros */ + if (length == 15) { + for (i = 0; i < 64 - dec_count; i++) + *wp++ = 0; + break; + } + + for (i = 0; i < length; i++) + *wp++ = 0; + *wp++ = coeff; + dec_count += length + 1; + } + + wp = block; + + for (i = 0; i < 64; i++) { + int pos = zigzag[i]; + int y = pos / 8; + int x = pos % 8; + + dwht_out[x + y * 8] = *wp++; + } + *rlc_in = input; + return ret; +} + +static const int quant_table[] = { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 3, 6, + 2, 2, 2, 2, 2, 3, 6, 6, + 2, 2, 2, 2, 3, 6, 6, 6, + 2, 2, 2, 3, 6, 6, 6, 6, + 2, 2, 3, 6, 6, 6, 6, 8, +}; + +static const int quant_table_p[] = { + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 6, + 3, 3, 3, 3, 3, 3, 6, 6, + 3, 3, 3, 3, 3, 6, 6, 9, + 3, 3, 3, 3, 6, 6, 9, 9, + 3, 3, 3, 6, 6, 9, 9, 10, +}; + +static void quantize_intra(s16 *coeff, s16 *de_coeff) +{ + const int *quant = quant_table; + int i, j; + + for (j = 0; j < 8; j++) { + for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) { + *coeff >>= *quant; + if (*coeff >= -DEADZONE_WIDTH && + *coeff <= DEADZONE_WIDTH) + *coeff = *de_coeff = 0; + else + *de_coeff = *coeff << *quant; + } + } +} + +static void dequantize_intra(s16 *coeff) +{ + const int *quant = quant_table; + int i, j; + + for (j = 0; j < 8; j++) + for (i = 0; i < 8; i++, quant++, coeff++) + *coeff <<= *quant; +} + +static void quantize_inter(s16 *coeff, s16 *de_coeff) +{ + const int *quant = quant_table_p; + int i, j; + + for (j = 0; j < 8; j++) { + for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) { + *coeff >>= *quant; + if (*coeff >= -DEADZONE_WIDTH && + *coeff <= DEADZONE_WIDTH) + *coeff = *de_coeff = 0; + else + *de_coeff = *coeff << *quant; + } + } +} + +static void dequantize_inter(s16 *coeff) +{ + const int *quant = quant_table_p; + int i, j; + + for (j = 0; j < 8; j++) + for (i = 0; i < 8; i++, quant++, coeff++) + *coeff <<= *quant; +} + +static void fwht(const u8 *block, s16 *output_block, unsigned int stride, + unsigned int input_step, bool intra) +{ + /* we'll need more than 8 bits for the transformed coefficients */ + s32 workspace1[8], workspace2[8]; + const u8 *tmp = block; + s16 *out = output_block; + int add = intra ? 256 : 0; + unsigned int i; + + /* stage 1 */ + stride *= input_step; + + for (i = 0; i < 8; i++, tmp += stride, out += 8) { + if (input_step == 1) { + workspace1[0] = tmp[0] + tmp[1] - add; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3] - add; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5] - add; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7] - add; + workspace1[7] = tmp[6] - tmp[7]; + } else { + workspace1[0] = tmp[0] + tmp[2] - add; + workspace1[1] = tmp[0] - tmp[2]; + + workspace1[2] = tmp[4] + tmp[6] - add; + workspace1[3] = tmp[4] - tmp[6]; + + workspace1[4] = tmp[8] + tmp[10] - add; + workspace1[5] = tmp[8] - tmp[10]; + + workspace1[6] = tmp[12] + tmp[14] - add; + workspace1[7] = tmp[12] - tmp[14]; + } + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1 * 8]; + workspace1[1] = out[0] - out[1 * 8]; + + workspace1[2] = out[2 * 8] + out[3 * 8]; + workspace1[3] = out[2 * 8] - out[3 * 8]; + + workspace1[4] = out[4 * 8] + out[5 * 8]; + workspace1[5] = out[4 * 8] - out[5 * 8]; + + workspace1[6] = out[6 * 8] + out[7 * 8]; + workspace1[7] = out[6 * 8] - out[7 * 8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + /* stage 3 */ + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + } +} + +/* + * Not the nicest way of doing it, but P-blocks get twice the range of + * that of the I-blocks. Therefore we need a type bigger than 8 bits. + * Furthermore values can be negative... This is just a version that + * works with 16 signed data + */ +static void fwht16(const s16 *block, s16 *output_block, int stride, int intra) +{ + /* we'll need more than 8 bits for the transformed coefficients */ + s32 workspace1[8], workspace2[8]; + const s16 *tmp = block; + s16 *out = output_block; + int i; + + for (i = 0; i < 8; i++, tmp += stride, out += 8) { + /* stage 1 */ + workspace1[0] = tmp[0] + tmp[1]; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3]; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5]; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7]; + workspace1[7] = tmp[6] - tmp[7]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1*8]; + workspace1[1] = out[0] - out[1*8]; + + workspace1[2] = out[2*8] + out[3*8]; + workspace1[3] = out[2*8] - out[3*8]; + + workspace1[4] = out[4*8] + out[5*8]; + workspace1[5] = out[4*8] - out[5*8]; + + workspace1[6] = out[6*8] + out[7*8]; + workspace1[7] = out[6*8] - out[7*8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0*8] = workspace2[0] + workspace2[4]; + out[1*8] = workspace2[0] - workspace2[4]; + out[2*8] = workspace2[1] - workspace2[5]; + out[3*8] = workspace2[1] + workspace2[5]; + out[4*8] = workspace2[2] + workspace2[6]; + out[5*8] = workspace2[2] - workspace2[6]; + out[6*8] = workspace2[3] - workspace2[7]; + out[7*8] = workspace2[3] + workspace2[7]; + } +} + +static void ifwht(const s16 *block, s16 *output_block, int intra) +{ + /* + * we'll need more than 8 bits for the transformed coefficients + * use native unit of cpu + */ + int workspace1[8], workspace2[8]; + int inter = intra ? 0 : 1; + const s16 *tmp = block; + s16 *out = output_block; + int i; + + for (i = 0; i < 8; i++, tmp += 8, out += 8) { + /* stage 1 */ + workspace1[0] = tmp[0] + tmp[1]; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3]; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5]; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7]; + workspace1[7] = tmp[6] - tmp[7]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1 * 8]; + workspace1[1] = out[0] - out[1 * 8]; + + workspace1[2] = out[2 * 8] + out[3 * 8]; + workspace1[3] = out[2 * 8] - out[3 * 8]; + + workspace1[4] = out[4 * 8] + out[5 * 8]; + workspace1[5] = out[4 * 8] - out[5 * 8]; + + workspace1[6] = out[6 * 8] + out[7 * 8]; + workspace1[7] = out[6 * 8] - out[7 * 8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + if (inter) { + int d; + + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + + for (d = 0; d < 8; d++) + out[8 * d] >>= 6; + } else { + int d; + + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + + for (d = 0; d < 8; d++) { + out[8 * d] >>= 6; + out[8 * d] += 128; + } + } + } +} + +static void fill_encoder_block(const u8 *input, s16 *dst, + unsigned int stride, unsigned int input_step) +{ + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++, input += input_step) + *dst++ = *input; + input += (stride - 8) * input_step; + } +} + +static int var_intra(const s16 *input) +{ + int32_t mean = 0; + int32_t ret = 0; + const s16 *tmp = input; + int i; + + for (i = 0; i < 8 * 8; i++, tmp++) + mean += *tmp; + mean /= 64; + tmp = input; + for (i = 0; i < 8 * 8; i++, tmp++) + ret += (*tmp - mean) < 0 ? -(*tmp - mean) : (*tmp - mean); + return ret; +} + +static int var_inter(const s16 *old, const s16 *new) +{ + int32_t ret = 0; + int i; + + for (i = 0; i < 8 * 8; i++, old++, new++) + ret += (*old - *new) < 0 ? -(*old - *new) : (*old - *new); + return ret; +} + +static int decide_blocktype(const u8 *current, const u8 *reference, + s16 *deltablock, unsigned int stride, + unsigned int input_step) +{ + s16 tmp[64]; + s16 old[64]; + s16 *work = tmp; + unsigned int k, l; + int vari; + int vard; + + fill_encoder_block(current, tmp, stride, input_step); + fill_encoder_block(reference, old, 8, 1); + vari = var_intra(tmp); + + for (k = 0; k < 8; k++) { + for (l = 0; l < 8; l++) { + *deltablock = *work - *reference; + deltablock++; + work++; + reference++; + } + } + deltablock -= 64; + vard = var_inter(old, tmp); + return vari <= vard ? IBLOCK : PBLOCK; +} + +static void fill_decoder_block(u8 *dst, const s16 *input, int stride) +{ + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) + *dst++ = *input++; + dst += stride - 8; + } +} + +static void add_deltas(s16 *deltas, const u8 *ref, int stride) +{ + int k, l; + + for (k = 0; k < 8; k++) { + for (l = 0; l < 8; l++) { + *deltas += *ref++; + /* + * Due to quantizing, it might possible that the + * decoded coefficients are slightly out of range + */ + if (*deltas < 0) + *deltas = 0; + else if (*deltas > 255) + *deltas = 255; + deltas++; + } + ref += stride - 8; + } +} + +static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, + struct cframe *cf, u32 height, u32 width, + unsigned int input_step, + bool is_intra, bool next_is_intra) +{ + u8 *input_start = input; + __be16 *rlco_start = *rlco; + s16 deltablock[64]; + __be16 pframe_bit = htons(PFRAME_BIT); + u32 encoding = 0; + unsigned int last_size = 0; + unsigned int i, j; + + for (j = 0; j < height / 8; j++) { + for (i = 0; i < width / 8; i++) { + /* intra code, first frame is always intra coded. */ + int blocktype = IBLOCK; + unsigned int size; + + if (!is_intra) + blocktype = decide_blocktype(input, refp, + deltablock, width, input_step); + if (is_intra || blocktype == IBLOCK) { + fwht(input, cf->coeffs, width, input_step, 1); + quantize_intra(cf->coeffs, cf->de_coeffs); + blocktype = IBLOCK; + } else { + /* inter code */ + encoding |= FRAME_PCODED; + fwht16(deltablock, cf->coeffs, 8, 0); + quantize_inter(cf->coeffs, cf->de_coeffs); + } + if (!next_is_intra) { + ifwht(cf->de_coeffs, cf->de_fwht, blocktype); + + if (blocktype == PBLOCK) + add_deltas(cf->de_fwht, refp, 8); + fill_decoder_block(refp, cf->de_fwht, 8); + } + + input += 8 * input_step; + refp += 8 * 8; + + if (encoding & FRAME_UNENCODED) + continue; + + size = rlc(cf->coeffs, *rlco, blocktype); + if (last_size == size && + !memcmp(*rlco + 1, *rlco - size + 1, 2 * size - 2)) { + __be16 *last_rlco = *rlco - size; + s16 hdr = ntohs(*last_rlco); + + if (!((*last_rlco ^ **rlco) & pframe_bit) && + (hdr & DUPS_MASK) < DUPS_MASK) + *last_rlco = htons(hdr + 2); + else + *rlco += size; + } else { + *rlco += size; + } + if (*rlco >= rlco_max) + encoding |= FRAME_UNENCODED; + last_size = size; + } + input += width * 7 * input_step; + } + if (encoding & FRAME_UNENCODED) { + u8 *out = (u8 *)rlco_start; + + input = input_start; + /* + * The compressed stream should never contain the magic + * header, so when we copy the YUV data we replace 0xff + * by 0xfe. Since YUV is limited range such values + * shouldn't appear anyway. + */ + for (i = 0; i < height * width; i++, input += input_step) + *out++ = (*input == 0xff) ? 0xfe : *input; + *rlco = (__be16 *)out; + } + return encoding; +} + +u32 encode_frame(struct raw_frame *frm, struct raw_frame *ref_frm, + struct cframe *cf, bool is_intra, bool next_is_intra) +{ + unsigned int size = frm->height * frm->width; + __be16 *rlco = cf->rlc_data; + __be16 *rlco_max; + u32 encoding; + + rlco_max = rlco + size / 2 - 256; + encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf, + frm->height, frm->width, + 1, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= LUMA_UNENCODED; + encoding &= ~FRAME_UNENCODED; + rlco_max = rlco + size / 8 - 256; + encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max, cf, + frm->height / 2, frm->width / 2, + frm->chroma_step, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= CB_UNENCODED; + encoding &= ~FRAME_UNENCODED; + rlco_max = rlco + size / 8 - 256; + encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max, cf, + frm->height / 2, frm->width / 2, + frm->chroma_step, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= CR_UNENCODED; + encoding &= ~FRAME_UNENCODED; + cf->size = (rlco - cf->rlc_data) * sizeof(*rlco); + return encoding; +} + +static void decode_plane(struct cframe *cf, const __be16 **rlco, u8 *ref, + u32 height, u32 width, bool uncompressed) +{ + unsigned int copies = 0; + s16 copy[8 * 8]; + s16 stat; + unsigned int i, j; + + if (uncompressed) { + memcpy(ref, *rlco, width * height); + *rlco += width * height / 2; + return; + } + + /* + * When decoding each macroblock the rlco pointer will be increased + * by 65 * 2 bytes worst-case. + * To avoid overflow the buffer has to be 65/64th of the actual raw + * image size, just in case someone feeds it malicious data. + */ + for (j = 0; j < height / 8; j++) { + for (i = 0; i < width / 8; i++) { + u8 *refp = ref + j * 8 * width + i * 8; + + if (copies) { + memcpy(cf->de_fwht, copy, sizeof(copy)); + if (stat & PFRAME_BIT) + add_deltas(cf->de_fwht, refp, width); + fill_decoder_block(refp, cf->de_fwht, width); + copies--; + continue; + } + + stat = derlc(rlco, cf->coeffs); + + if (stat & PFRAME_BIT) + dequantize_inter(cf->coeffs); + else + dequantize_intra(cf->coeffs); + + ifwht(cf->coeffs, cf->de_fwht, + (stat & PFRAME_BIT) ? 0 : 1); + + copies = (stat & DUPS_MASK) >> 1; + if (copies) + memcpy(copy, cf->de_fwht, sizeof(copy)); + if (stat & PFRAME_BIT) + add_deltas(cf->de_fwht, refp, width); + fill_decoder_block(refp, cf->de_fwht, width); + } + } +} + +void decode_frame(struct cframe *cf, struct raw_frame *ref, u32 hdr_flags) +{ + const __be16 *rlco = cf->rlc_data; + + decode_plane(cf, &rlco, ref->luma, cf->height, cf->width, + hdr_flags & VICODEC_FL_LUMA_IS_UNCOMPRESSED); + decode_plane(cf, &rlco, ref->cb, cf->height / 2, cf->width / 2, + hdr_flags & VICODEC_FL_CB_IS_UNCOMPRESSED); + decode_plane(cf, &rlco, ref->cr, cf->height / 2, cf->width / 2, + hdr_flags & VICODEC_FL_CR_IS_UNCOMPRESSED); +} diff --git a/drivers/media/platform/vicodec/vicodec-codec.h b/drivers/media/platform/vicodec/vicodec-codec.h new file mode 100644 index 000000000000..cdfad1332a3e --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-codec.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2016 Tom aan de Wiel + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef VICODEC_RLC_H +#define VICODEC_RLC_H + +#include +#include +#include + +/* + * The compressed format consists of a cframe_hdr struct followed by the + * compressed frame data. The header contains the size of that data. + * Each Y, Cb and Cr plane is compressed separately. If the compressed + * size of each plane becomes larger than the uncompressed size, then + * that plane is stored uncompressed and the corresponding bit is set + * in the flags field of the header. + * + * Each compressed plane consists of macroblocks and each macroblock + * is run-length-encoded. Each macroblock starts with a 16 bit value. + * Bit 15 indicates if this is a P-coded macroblock (1) or not (0). + * P-coded macroblocks contain a delta against the previous frame. + * + * Bits 1-12 contain a number. If non-zero, then this same macroblock + * repeats that number of times. This results in a high degree of + * compression for generated images like colorbars. + * + * Following this macroblock header the MB coefficients are run-length + * encoded: the top 12 bits contain the coefficient, the bottom 4 bits + * tell how many times this coefficient occurs. The value 0xf indicates + * that the remainder of the macroblock should be filled with zeroes. + * + * All 16 and 32 bit values are stored in big-endian (network) order. + * + * Each cframe_hdr starts with an 8 byte magic header that is + * guaranteed not to occur in the compressed frame data. This header + * can be used to sync to the next frame. + * + * This codec uses the Fast Walsh Hadamard Transform. Tom aan de Wiel + * developed this as part of a university project, specifically for use + * with this driver. His project report can be found here: + * + * https://hverkuil.home.xs4all.nl/fwht.pdf + */ + +/* + * Note: bit 0 of the header must always be 0. Otherwise it cannot + * be guaranteed that the magic 8 byte sequence (see below) can + * never occur in the rlc output. + */ +#define PFRAME_BIT (1 << 15) +#define DUPS_MASK 0x1ffe + +/* + * This is a sequence of 8 bytes with the low 4 bits set to 0xf. + * + * This sequence cannot occur in the encoded data + */ +#define VICODEC_MAGIC1 0x4f4f4f4f +#define VICODEC_MAGIC2 0xffffffff + +#define VICODEC_VERSION 1 + +#define VICODEC_MAX_WIDTH 3840 +#define VICODEC_MAX_HEIGHT 2160 +#define VICODEC_MIN_WIDTH 640 +#define VICODEC_MIN_HEIGHT 480 + +#define PBLOCK 0 +#define IBLOCK 1 + +/* Set if this is an interlaced format */ +#define VICODEC_FL_IS_INTERLACED BIT(0) +/* Set if this is a bottom-first (NTSC) interlaced format */ +#define VICODEC_FL_IS_BOTTOM_FIRST BIT(1) +/* Set if each 'frame' contains just one field */ +#define VICODEC_FL_IS_ALTERNATE BIT(2) +/* + * If VICODEC_FL_IS_ALTERNATE was set, then this is set if this + * 'frame' is the bottom field, else it is the top field. + */ +#define VICODEC_FL_IS_BOTTOM_FIELD BIT(3) +/* Set if this frame is uncompressed */ +#define VICODEC_FL_LUMA_IS_UNCOMPRESSED BIT(4) +#define VICODEC_FL_CB_IS_UNCOMPRESSED BIT(5) +#define VICODEC_FL_CR_IS_UNCOMPRESSED BIT(6) + +struct cframe_hdr { + u32 magic1; + u32 magic2; + __be32 version; + __be32 width, height; + __be32 flags; + __be32 colorspace; + __be32 xfer_func; + __be32 ycbcr_enc; + __be32 quantization; + __be32 size; +}; + +struct cframe { + unsigned int width, height; + __be16 *rlc_data; + s16 coeffs[8 * 8]; + s16 de_coeffs[8 * 8]; + s16 de_fwht[8 * 8]; + u32 size; +}; + +struct raw_frame { + unsigned int width, height; + unsigned int chroma_step; + u8 *luma, *cb, *cr; +}; + +#define FRAME_PCODED BIT(0) +#define FRAME_UNENCODED BIT(1) +#define LUMA_UNENCODED BIT(2) +#define CB_UNENCODED BIT(3) +#define CR_UNENCODED BIT(4) + +u32 encode_frame(struct raw_frame *frm, struct raw_frame *ref_frm, + struct cframe *cf, bool is_intra, bool next_is_intra); +void decode_frame(struct cframe *cf, struct raw_frame *ref, u32 hdr_flags); + +#endif -- cgit v1.2.3-70-g09d2 From 256bf813ba39f7f9277a5cd05b5c152dbbaf4aae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Jul 2018 03:52:50 -0400 Subject: media: vicodec: add the virtual codec driver Add the virtual codec driver that uses the Fast Walsh Hadamard Transform. Keiichi Watanabe contributed the multiplanar support. Signed-off-by: Hans Verkuil Co-Developed-by: Keiichi Watanabe Signed-off-by: Keiichi Watanabe Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 + drivers/media/platform/Kconfig | 3 + drivers/media/platform/Makefile | 1 + drivers/media/platform/vicodec/Kconfig | 13 + drivers/media/platform/vicodec/Makefile | 4 + drivers/media/platform/vicodec/vicodec-core.c | 1506 +++++++++++++++++++++++++ 6 files changed, 1535 insertions(+) create mode 100644 drivers/media/platform/vicodec/Kconfig create mode 100644 drivers/media/platform/vicodec/Makefile create mode 100644 drivers/media/platform/vicodec/vicodec-core.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index bbd9b9b3d74f..67781db8b077 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15153,6 +15153,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/via/via-velocity.* +VICODEC VIRTUAL CODEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: https://linuxtv.org +S: Maintained +F: drivers/media/platform/vicodec/* + VIDEO MULTIPLEXER DRIVER M: Philipp Zabel L: linux-media@vger.kernel.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 84cb97eccb2a..40bd3d96ca82 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -514,6 +514,9 @@ config VIDEO_VIM2M ---help--- This is a virtual test device for the memory-to-memory driver framework. + +source "drivers/media/platform/vicodec/Kconfig" + endif #V4L_TEST_DRIVERS menuconfig DVB_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 04bc1502a30e..8e5f88eb7559 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIMC) += vimc/ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o +obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/ diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig new file mode 100644 index 000000000000..2503bcb1529f --- /dev/null +++ b/drivers/media/platform/vicodec/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_VICODEC + tristate "Virtual Codec Driver" + depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + default n + help + Driver for a Virtual Codec + + This driver can be compared to the vim2m driver for emulating + a video device node that exposes an emulated hardware codec. + + When in doubt, say N. diff --git a/drivers/media/platform/vicodec/Makefile b/drivers/media/platform/vicodec/Makefile new file mode 100644 index 000000000000..197229428953 --- /dev/null +++ b/drivers/media/platform/vicodec/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +vicodec-objs := vicodec-core.o vicodec-codec.o + +obj-$(CONFIG_VIDEO_VICODEC) += vicodec.o diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c new file mode 100644 index 000000000000..408cd55d3580 --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * A virtual codec example device. + * + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This is a virtual codec device driver for testing the codec framework. + * It simulates a device that uses memory buffers for both source and + * destination and encodes or decodes the data. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "vicodec-codec.h" + +MODULE_DESCRIPTION("Virtual codec device"); +MODULE_AUTHOR("Hans Verkuil "); +MODULE_LICENSE("GPL v2"); + +static bool multiplanar; +module_param(multiplanar, bool, 0444); +MODULE_PARM_DESC(multiplanar, + " use multi-planar API instead of single-planar API"); + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, " activates debug info"); + +#define VICODEC_NAME "vicodec" +#define MAX_WIDTH 4096U +#define MIN_WIDTH 640U +#define MAX_HEIGHT 2160U +#define MIN_HEIGHT 480U + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + + +static void vicodec_dev_release(struct device *dev) +{ +} + +static struct platform_device vicodec_pdev = { + .name = VICODEC_NAME, + .dev.release = vicodec_dev_release, +}; + +/* Per-queue, driver-specific private data */ +struct vicodec_q_data { + unsigned int width; + unsigned int height; + unsigned int flags; + unsigned int sizeimage; + unsigned int sequence; + u32 fourcc; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +struct vicodec_dev { + struct v4l2_device v4l2_dev; + struct video_device enc_vfd; + struct video_device dec_vfd; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif + + struct mutex enc_mutex; + struct mutex dec_mutex; + spinlock_t enc_lock; + spinlock_t dec_lock; + + struct v4l2_m2m_dev *enc_dev; + struct v4l2_m2m_dev *dec_dev; +}; + +struct vicodec_ctx { + struct v4l2_fh fh; + struct vicodec_dev *dev; + bool is_enc; + spinlock_t *lock; + + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *ctrl_gop_size; + unsigned int gop_size; + unsigned int gop_cnt; + + /* Abort requested by m2m */ + int aborting; + struct vb2_v4l2_buffer *last_src_buf; + struct vb2_v4l2_buffer *last_dst_buf; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_xfer_func xfer_func; + enum v4l2_quantization quantization; + + /* Source and destination queue data */ + struct vicodec_q_data q_data[2]; + struct raw_frame ref_frame; + u8 *compressed_frame; + u32 cur_buf_offset; + u32 comp_max_size; + u32 comp_size; + u32 comp_magic_cnt; + u32 comp_frame_size; + bool comp_has_frame; + bool comp_has_next_frame; +}; + +static const u32 pixfmts_yuv[] = { + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, +}; + +static inline struct vicodec_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct vicodec_ctx, fh); +} + +static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return &ctx->q_data[V4L2_M2M_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return &ctx->q_data[V4L2_M2M_DST]; + default: + WARN_ON(1); + break; + } + return NULL; +} + +static void encode(struct vicodec_ctx *ctx, + struct vicodec_q_data *q_data, + u8 *p_in, u8 *p_out) +{ + unsigned int size = q_data->width * q_data->height; + struct cframe_hdr *p_hdr; + struct cframe cf; + struct raw_frame rf; + u32 encoding; + + rf.width = q_data->width; + rf.height = q_data->height; + rf.luma = p_in; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUV420: + rf.cb = rf.luma + size; + rf.cr = rf.cb + size / 4; + rf.chroma_step = 1; + break; + case V4L2_PIX_FMT_YVU420: + rf.cr = rf.luma + size; + rf.cb = rf.cr + size / 4; + rf.chroma_step = 1; + break; + case V4L2_PIX_FMT_NV12: + rf.cb = rf.luma + size; + rf.cr = rf.cb + 1; + rf.chroma_step = 2; + break; + case V4L2_PIX_FMT_NV21: + rf.cr = rf.luma + size; + rf.cb = rf.cr + 1; + rf.chroma_step = 2; + break; + } + + cf.width = q_data->width; + cf.height = q_data->height; + cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr)); + + encoding = encode_frame(&rf, &ctx->ref_frame, &cf, !ctx->gop_cnt, + ctx->gop_cnt == ctx->gop_size - 1); + if (encoding != FRAME_PCODED) + ctx->gop_cnt = 0; + if (++ctx->gop_cnt == ctx->gop_size) + ctx->gop_cnt = 0; + + p_hdr = (struct cframe_hdr *)p_out; + p_hdr->magic1 = VICODEC_MAGIC1; + p_hdr->magic2 = VICODEC_MAGIC2; + p_hdr->version = htonl(VICODEC_VERSION); + p_hdr->width = htonl(cf.width); + p_hdr->height = htonl(cf.height); + p_hdr->flags = htonl(q_data->flags); + if (encoding & LUMA_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_LUMA_IS_UNCOMPRESSED); + if (encoding & CB_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_CB_IS_UNCOMPRESSED); + if (encoding & CR_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_CR_IS_UNCOMPRESSED); + p_hdr->colorspace = htonl(ctx->colorspace); + p_hdr->xfer_func = htonl(ctx->xfer_func); + p_hdr->ycbcr_enc = htonl(ctx->ycbcr_enc); + p_hdr->quantization = htonl(ctx->quantization); + p_hdr->size = htonl(cf.size); + ctx->ref_frame.width = cf.width; + ctx->ref_frame.height = cf.height; +} + +static int decode(struct vicodec_ctx *ctx, + struct vicodec_q_data *q_data, + u8 *p_in, u8 *p_out) +{ + unsigned int size = q_data->width * q_data->height; + unsigned int i; + struct cframe_hdr *p_hdr; + struct cframe cf; + u8 *p; + + p_hdr = (struct cframe_hdr *)p_in; + cf.width = ntohl(p_hdr->width); + cf.height = ntohl(p_hdr->height); + q_data->flags = ntohl(p_hdr->flags); + ctx->colorspace = ntohl(p_hdr->colorspace); + ctx->xfer_func = ntohl(p_hdr->xfer_func); + ctx->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); + ctx->quantization = ntohl(p_hdr->quantization); + cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); + + if (p_hdr->magic1 != VICODEC_MAGIC1 || + p_hdr->magic2 != VICODEC_MAGIC2 || + ntohl(p_hdr->version) != VICODEC_VERSION || + cf.width < VICODEC_MIN_WIDTH || + cf.width > VICODEC_MAX_WIDTH || + cf.height < VICODEC_MIN_HEIGHT || + cf.height > VICODEC_MAX_HEIGHT || + (cf.width & 7) || (cf.height & 7)) + return -EINVAL; + + /* TODO: support resolution changes */ + if (cf.width != q_data->width || cf.height != q_data->height) + return -EINVAL; + + decode_frame(&cf, &ctx->ref_frame, q_data->flags); + memcpy(p_out, ctx->ref_frame.luma, size); + p_out += size; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUV420: + memcpy(p_out, ctx->ref_frame.cb, size / 4); + p_out += size / 4; + memcpy(p_out, ctx->ref_frame.cr, size / 4); + break; + case V4L2_PIX_FMT_YVU420: + memcpy(p_out, ctx->ref_frame.cr, size / 4); + p_out += size / 4; + memcpy(p_out, ctx->ref_frame.cb, size / 4); + break; + case V4L2_PIX_FMT_NV12: + for (i = 0, p = p_out; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cb[i]; + for (i = 0, p = p_out + 1; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cr[i]; + break; + case V4L2_PIX_FMT_NV21: + for (i = 0, p = p_out; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cr[i]; + for (i = 0, p = p_out + 1; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cb[i]; + break; + } + return 0; +} + +static int device_process(struct vicodec_ctx *ctx, + struct vb2_v4l2_buffer *in_vb, + struct vb2_v4l2_buffer *out_vb) +{ + struct vicodec_dev *dev = ctx->dev; + struct vicodec_q_data *q_out, *q_cap; + u8 *p_in, *p_out; + int ret; + + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ctx->is_enc) + p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); + else + p_in = ctx->compressed_frame; + p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); + if (!p_in || !p_out) { + v4l2_err(&dev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return -EFAULT; + } + + if (ctx->is_enc) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)p_out; + + encode(ctx, q_out, p_in, p_out); + vb2_set_plane_payload(&out_vb->vb2_buf, 0, + sizeof(*p_hdr) + ntohl(p_hdr->size)); + } else { + ret = decode(ctx, q_cap, p_in, p_out); + if (ret) + return ret; + vb2_set_plane_payload(&out_vb->vb2_buf, 0, + q_cap->width * q_cap->height * 3 / 2); + } + + out_vb->sequence = q_cap->sequence++; + out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp; + + if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE) + out_vb->timecode = in_vb->timecode; + out_vb->field = in_vb->field; + out_vb->flags &= ~V4L2_BUF_FLAG_LAST; + out_vb->flags |= in_vb->flags & + (V4L2_BUF_FLAG_TIMECODE | + V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + + return 0; +} + +/* + * mem2mem callbacks + */ + +/* device_run() - prepares and starts the device */ +static void device_run(void *priv) +{ + static const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + struct vicodec_ctx *ctx = priv; + struct vicodec_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct vicodec_q_data *q_out; + u32 state; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + state = VB2_BUF_STATE_DONE; + if (device_process(ctx, src_buf, dst_buf)) + state = VB2_BUF_STATE_ERROR; + ctx->last_dst_buf = dst_buf; + + spin_lock(ctx->lock); + if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + if (ctx->is_enc) { + src_buf->sequence = q_out->sequence++; + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, state); + } else if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == ctx->cur_buf_offset) { + src_buf->sequence = q_out->sequence++; + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, state); + ctx->cur_buf_offset = 0; + ctx->comp_has_next_frame = false; + } + v4l2_m2m_buf_done(dst_buf, state); + ctx->comp_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = false; + spin_unlock(ctx->lock); + + if (ctx->is_enc) + v4l2_m2m_job_finish(dev->enc_dev, ctx->fh.m2m_ctx); + else + v4l2_m2m_job_finish(dev->dec_dev, ctx->fh.m2m_ctx); +} + +static void job_remove_out_buf(struct vicodec_ctx *ctx, u32 state) +{ + struct vb2_v4l2_buffer *src_buf; + struct vicodec_q_data *q_out; + + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + spin_lock(ctx->lock); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + src_buf->sequence = q_out->sequence++; + v4l2_m2m_buf_done(src_buf, state); + ctx->cur_buf_offset = 0; + spin_unlock(ctx->lock); +} + +static int job_ready(void *priv) +{ + static const u8 magic[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff + }; + struct vicodec_ctx *ctx = priv; + struct vb2_v4l2_buffer *src_buf; + u8 *p_out; + u8 *p; + u32 sz; + u32 state; + + if (ctx->is_enc || ctx->comp_has_frame) + return 1; + +restart: + ctx->comp_has_next_frame = false; + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + if (!src_buf) + return 0; + p_out = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + sz = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + p = p_out + ctx->cur_buf_offset; + + state = VB2_BUF_STATE_DONE; + + if (!ctx->comp_size) { + state = VB2_BUF_STATE_ERROR; + for (; p < p_out + sz; p++) { + u32 copy; + + p = memchr(p, magic[ctx->comp_magic_cnt], sz); + if (!p) { + ctx->comp_magic_cnt = 0; + break; + } + copy = sizeof(magic) - ctx->comp_magic_cnt; + if (p_out + sz - p < copy) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_magic_cnt, + p, copy); + ctx->comp_magic_cnt += copy; + if (!memcmp(ctx->compressed_frame, magic, ctx->comp_magic_cnt)) { + p += copy; + state = VB2_BUF_STATE_DONE; + break; + } + ctx->comp_magic_cnt = 0; + } + if (ctx->comp_magic_cnt < sizeof(magic)) { + job_remove_out_buf(ctx, state); + goto restart; + } + ctx->comp_size = sizeof(magic); + } + if (ctx->comp_size < sizeof(struct cframe_hdr)) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)ctx->compressed_frame; + u32 copy = sizeof(struct cframe_hdr) - ctx->comp_size; + + if (copy > p_out + sz - p) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_size, + p, copy); + p += copy; + ctx->comp_size += copy; + if (ctx->comp_size < sizeof(struct cframe_hdr)) { + job_remove_out_buf(ctx, state); + goto restart; + } + ctx->comp_frame_size = ntohl(p_hdr->size) + sizeof(*p_hdr); + if (ctx->comp_frame_size > ctx->comp_max_size) + ctx->comp_frame_size = ctx->comp_max_size; + } + if (ctx->comp_size < ctx->comp_frame_size) { + u32 copy = ctx->comp_frame_size - ctx->comp_size; + + if (copy > p_out + sz - p) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_size, + p, copy); + p += copy; + ctx->comp_size += copy; + if (ctx->comp_size < ctx->comp_frame_size) { + job_remove_out_buf(ctx, state); + goto restart; + } + } + ctx->cur_buf_offset = p - p_out; + ctx->comp_has_frame = true; + ctx->comp_has_next_frame = false; + if (sz - ctx->cur_buf_offset >= sizeof(struct cframe_hdr)) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)p; + u32 frame_size = ntohl(p_hdr->size); + u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); + + if (!memcmp(p, magic, sizeof(magic))) + ctx->comp_has_next_frame = remaining >= frame_size; + } + return 1; +} + +static void job_abort(void *priv) +{ + struct vicodec_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +/* + * video ioctls + */ + +static u32 find_fmt(u32 fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pixfmts_yuv); i++) + if (pixfmts_yuv[i] == fmt) + return fmt; + return pixfmts_yuv[0]; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, VICODEC_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, VICODEC_NAME, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", VICODEC_NAME); + cap->device_caps = V4L2_CAP_STREAMING | + (multiplanar ? + V4L2_CAP_VIDEO_M2M_MPLANE : + V4L2_CAP_VIDEO_M2M); + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) +{ + bool is_yuv = (is_enc && is_out) || (!is_enc && !is_out); + + if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar) + return -EINVAL; + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && multiplanar) + return -EINVAL; + if (f->index >= (is_yuv ? ARRAY_SIZE(pixfmts_yuv) : 1)) + return -EINVAL; + + if (is_yuv) + f->pixelformat = pixfmts_yuv[f->index]; + else + f->pixelformat = V4L2_PIX_FMT_FWHT; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + + return enum_fmt(f, ctx->is_enc, false); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + + return enum_fmt(f, ctx->is_enc, true); +} + +static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct vicodec_q_data *q_data; + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->width = q_data->width; + pix->height = q_data->height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = q_data->fourcc; + if (q_data->fourcc == V4L2_PIX_FMT_FWHT) + pix->bytesperline = 0; + else + pix->bytesperline = q_data->width; + pix->sizeimage = q_data->sizeimage; + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->width = q_data->width; + pix_mp->height = q_data->height; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = q_data->fourcc; + pix_mp->num_planes = 1; + if (q_data->fourcc == V4L2_PIX_FMT_FWHT) + pix_mp->plane_fmt[0].bytesperline = 0; + else + pix_mp->plane_fmt[0].bytesperline = q_data->width; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; + pix_mp->colorspace = ctx->colorspace; + pix_mp->xfer_func = ctx->xfer_func; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(file2ctx(file), f); +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(file2ctx(file), f); +} + +static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH) & ~7; + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + pix->bytesperline = pix->width; + pix->sizeimage = pix->width * pix->height * 3 / 2; + pix->field = V4L2_FIELD_NONE; + if (pix->pixelformat == V4L2_PIX_FMT_FWHT) { + pix->bytesperline = 0; + pix->sizeimage += sizeof(struct cframe_hdr); + } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH) & ~7; + pix_mp->height = + clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + pix_mp->plane_fmt[0].bytesperline = pix_mp->width; + pix_mp->plane_fmt[0].sizeimage = + pix_mp->width * pix_mp->height * 3 / 2; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->num_planes = 1; + if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) { + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage += + sizeof(struct cframe_hdr); + } + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(f->fmt.pix.pixelformat); + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix_mp->pixelformat); + pix_mp->colorspace = ctx->colorspace; + pix_mp->xfer_func = ctx->xfer_func; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + + return vidioc_try_fmt(ctx, f); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->pixelformat = !ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix->pixelformat); + if (!pix->colorspace) + pix->colorspace = V4L2_COLORSPACE_REC709; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->pixelformat = !ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix_mp->pixelformat); + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_REC709; + break; + default: + return -EINVAL; + } + + return vidioc_try_fmt(ctx, f); +} + +static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct vicodec_q_data *q_data; + struct vb2_queue *vq; + bool fmt_changed = true; + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) + fmt_changed = + q_data->fourcc != pix->pixelformat || + q_data->width != pix->width || + q_data->height != pix->height; + + if (vb2_is_busy(vq) && fmt_changed) + return -EBUSY; + + q_data->fourcc = pix->pixelformat; + q_data->width = pix->width; + q_data->height = pix->height; + q_data->sizeimage = pix->sizeimage; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) + fmt_changed = + q_data->fourcc != pix_mp->pixelformat || + q_data->width != pix_mp->width || + q_data->height != pix_mp->height; + + if (vb2_is_busy(vq) && fmt_changed) + return -EBUSY; + + q_data->fourcc = pix_mp->pixelformat; + q_data->width = pix_mp->width; + q_data->height = pix_mp->height; + q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; + break; + default: + return -EINVAL; + } + + dprintk(ctx->dev, + "Setting format for type %d, wxh: %dx%d, fourcc: %08x\n", + f->type, q_data->width, q_data->height, q_data->fourcc); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(file2ctx(file), f); +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + int ret; + + ret = vidioc_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = vidioc_s_fmt(file2ctx(file), f); + if (!ret) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + ctx->colorspace = pix->colorspace; + ctx->xfer_func = pix->xfer_func; + ctx->ycbcr_enc = pix->ycbcr_enc; + ctx->quantization = pix->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + ctx->colorspace = pix_mp->colorspace; + ctx->xfer_func = pix_mp->xfer_func; + ctx->ycbcr_enc = pix_mp->ycbcr_enc; + ctx->quantization = pix_mp->quantization; + break; + default: + break; + } + } + return ret; +} + +static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) +{ + static const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + spin_lock(ctx->lock); + ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (!ctx->last_src_buf && ctx->last_dst_buf) { + ctx->last_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + spin_unlock(ctx->lock); +} + +static int vicodec_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + if (ec->cmd != V4L2_ENC_CMD_STOP) + return -EINVAL; + + if (ec->flags & V4L2_ENC_CMD_STOP_AT_GOP_END) + return -EINVAL; + + return 0; +} + +static int vicodec_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + struct vicodec_ctx *ctx = file2ctx(file); + int ret; + + ret = vicodec_try_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + vicodec_mark_last_buf(ctx); + return 0; +} + +static int vicodec_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) + return -EINVAL; + + if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) + return -EINVAL; + + return 0; +} + +static int vicodec_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct vicodec_ctx *ctx = file2ctx(file); + int ret; + + ret = vicodec_try_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + vicodec_mark_last_buf(ctx); + return 0; +} + +static int vicodec_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_FWHT: + break; + default: + if (find_fmt(fsize->pixel_format) == fsize->pixel_format) + break; + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH; + fsize->stepwise.step_width = 8; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT; + fsize->stepwise.step_height = 8; + + return 0; +} + +static int vicodec_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = vicodec_try_encoder_cmd, + .vidioc_encoder_cmd = vicodec_encoder_cmd, + .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, + .vidioc_decoder_cmd = vicodec_decoder_cmd, + .vidioc_enum_framesizes = vicodec_enum_framesizes, + + .vidioc_subscribe_event = vicodec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + + +/* + * Queue operations + */ + +static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(vq); + struct vicodec_q_data *q_data = get_q_data(ctx, vq->type); + unsigned int size = q_data->sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + return 0; +} + +static int vicodec_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vicodec_q_data *q_data; + + dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dprintk(ctx->dev, "%s field isn't supported\n", + __func__); + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + dprintk(ctx->dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + return 0; +} + +static void vicodec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void vicodec_return_bufs(struct vb2_queue *q, u32 state) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (vbuf == NULL) + return; + spin_lock(ctx->lock); + v4l2_m2m_buf_done(vbuf, state); + spin_unlock(ctx->lock); + } +} + +static int vicodec_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + struct vicodec_q_data *q_data = get_q_data(ctx, q->type); + unsigned int size = q_data->width * q_data->height; + + q_data->sequence = 0; + + if (!V4L2_TYPE_IS_OUTPUT(q->type)) + return 0; + + ctx->ref_frame.width = ctx->ref_frame.height = 0; + ctx->ref_frame.luma = kvmalloc(size * 3 / 2, GFP_KERNEL); + ctx->comp_max_size = size * 3 / 2 + sizeof(struct cframe_hdr); + ctx->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); + if (!ctx->ref_frame.luma || !ctx->compressed_frame) { + kvfree(ctx->ref_frame.luma); + kvfree(ctx->compressed_frame); + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); + return -ENOMEM; + } + ctx->ref_frame.cb = ctx->ref_frame.luma + size; + ctx->ref_frame.cr = ctx->ref_frame.cb + size / 4; + ctx->last_src_buf = NULL; + ctx->last_dst_buf = NULL; + v4l2_ctrl_grab(ctx->ctrl_gop_size, true); + ctx->gop_size = v4l2_ctrl_g_ctrl(ctx->ctrl_gop_size); + ctx->gop_cnt = 0; + ctx->cur_buf_offset = 0; + ctx->comp_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = false; + + return 0; +} + +static void vicodec_stop_streaming(struct vb2_queue *q) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + + vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); + + if (!V4L2_TYPE_IS_OUTPUT(q->type)) + return; + + kvfree(ctx->ref_frame.luma); + kvfree(ctx->compressed_frame); + v4l2_ctrl_grab(ctx->ctrl_gop_size, false); +} + +static const struct vb2_ops vicodec_qops = { + .queue_setup = vicodec_queue_setup, + .buf_prepare = vicodec_buf_prepare, + .buf_queue = vicodec_buf_queue, + .start_streaming = vicodec_start_streaming, + .stop_streaming = vicodec_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct vicodec_ctx *ctx = priv; + int ret; + + src_vq->type = (multiplanar ? + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &vicodec_qops; + src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = ctx->is_enc ? &ctx->dev->enc_mutex : + &ctx->dev->dec_mutex; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = (multiplanar ? + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &vicodec_qops; + dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = src_vq->lock; + + return vb2_queue_init(dst_vq); +} + +/* + * File operations + */ +static int vicodec_open(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct vicodec_dev *dev = video_drvdata(file); + struct vicodec_ctx *ctx = NULL; + struct v4l2_ctrl_handler *hdl; + unsigned int size; + int rc = 0; + + if (mutex_lock_interruptible(vfd->lock)) + return -ERESTARTSYS; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + rc = -ENOMEM; + goto open_unlock; + } + + if (vfd == &dev->enc_vfd) + ctx->is_enc = true; + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + hdl = &ctx->hdl; + v4l2_ctrl_handler_init(hdl, 4); + ctx->ctrl_gop_size = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 16, 1, 10); + if (hdl->error) { + rc = hdl->error; + v4l2_ctrl_handler_free(hdl); + kfree(ctx); + goto open_unlock; + } + ctx->fh.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + ctx->q_data[V4L2_M2M_SRC].fourcc = + ctx->is_enc ? V4L2_PIX_FMT_YUV420 : V4L2_PIX_FMT_FWHT; + ctx->q_data[V4L2_M2M_SRC].width = 1280; + ctx->q_data[V4L2_M2M_SRC].height = 720; + size = 1280 * 720 * 3 / 2; + ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; + ctx->q_data[V4L2_M2M_DST].fourcc = + ctx->is_enc ? V4L2_PIX_FMT_FWHT : V4L2_PIX_FMT_YUV420; + ctx->colorspace = V4L2_COLORSPACE_REC709; + + size += sizeof(struct cframe_hdr); + if (ctx->is_enc) { + ctx->q_data[V4L2_M2M_DST].sizeimage = size; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->enc_dev, ctx, + &queue_init); + ctx->lock = &dev->enc_lock; + } else { + ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->dec_dev, ctx, + &queue_init); + ctx->lock = &dev->dec_lock; + } + + if (IS_ERR(ctx->fh.m2m_ctx)) { + rc = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_free(hdl); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + goto open_unlock; + } + + v4l2_fh_add(&ctx->fh); + +open_unlock: + mutex_unlock(vfd->lock); + return rc; +} + +static int vicodec_release(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct vicodec_ctx *ctx = file2ctx(file); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->hdl); + mutex_lock(vfd->lock); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + mutex_unlock(vfd->lock); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations vicodec_fops = { + .owner = THIS_MODULE, + .open = vicodec_open, + .release = vicodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device vicodec_videodev = { + .name = VICODEC_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &vicodec_fops, + .ioctl_ops = &vicodec_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_abort = job_abort, + .job_ready = job_ready, +}; + +static int vicodec_probe(struct platform_device *pdev) +{ + struct vicodec_dev *dev; + struct video_device *vfd; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->enc_lock); + spin_lock_init(&dev->dec_lock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &pdev->dev; + strlcpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->v4l2_dev.mdev = &dev->mdev; +#endif + + mutex_init(&dev->enc_mutex); + mutex_init(&dev->dec_mutex); + + platform_set_drvdata(pdev, dev); + + dev->enc_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->enc_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init vicodec device\n"); + ret = PTR_ERR(dev->enc_dev); + goto unreg_dev; + } + + dev->dec_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->dec_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init vicodec device\n"); + ret = PTR_ERR(dev->dec_dev); + goto err_enc_m2m; + } + + dev->enc_vfd = vicodec_videodev; + vfd = &dev->enc_vfd; + vfd->lock = &dev->enc_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + strlcpy(vfd->name, "vicodec-enc", sizeof(vfd->name)); + v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto err_dec_m2m; + } + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + dev->dec_vfd = vicodec_videodev; + vfd = &dev->dec_vfd; + vfd->lock = &dev->dec_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + strlcpy(vfd->name, "vicodec-dec", sizeof(vfd->name)); + v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto unreg_enc; + } + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = v4l2_m2m_register_media_controller(dev->enc_dev, + &dev->enc_vfd, MEDIA_ENT_F_PROC_VIDEO_ENCODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m; + } + + ret = v4l2_m2m_register_media_controller(dev->dec_dev, + &dev->dec_vfd, MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m_enc_mc; + } + + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); + goto unreg_m2m_dec_mc; + } +#endif + return 0; + +#ifdef CONFIG_MEDIA_CONTROLLER +unreg_m2m_dec_mc: + v4l2_m2m_unregister_media_controller(dev->dec_dev); +unreg_m2m_enc_mc: + v4l2_m2m_unregister_media_controller(dev->enc_dev); +unreg_m2m: + video_unregister_device(&dev->dec_vfd); +#endif +unreg_enc: + video_unregister_device(&dev->enc_vfd); +err_dec_m2m: + v4l2_m2m_release(dev->dec_dev); +err_enc_m2m: + v4l2_m2m_release(dev->enc_dev); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int vicodec_remove(struct platform_device *pdev) +{ + struct vicodec_dev *dev = platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " VICODEC_NAME); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->enc_dev); + v4l2_m2m_unregister_media_controller(dev->dec_dev); + media_device_cleanup(&dev->mdev); +#endif + + v4l2_m2m_release(dev->enc_dev); + v4l2_m2m_release(dev->dec_dev); + video_unregister_device(&dev->enc_vfd); + video_unregister_device(&dev->dec_vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +static struct platform_driver vicodec_pdrv = { + .probe = vicodec_probe, + .remove = vicodec_remove, + .driver = { + .name = VICODEC_NAME, + }, +}; + +static void __exit vicodec_exit(void) +{ + platform_driver_unregister(&vicodec_pdrv); + platform_device_unregister(&vicodec_pdev); +} + +static int __init vicodec_init(void) +{ + int ret; + + ret = platform_device_register(&vicodec_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&vicodec_pdrv); + if (ret) + platform_device_unregister(&vicodec_pdev); + + return ret; +} + +module_init(vicodec_init); +module_exit(vicodec_exit); -- cgit v1.2.3-70-g09d2 From 5c9072e8458f937a9c0a38736989d128ef990fc5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 06:57:52 -0400 Subject: media: coda: move framebuffer size calculation out of loop All internal YCbCr frame buffers are the same size, calculate ycbcr_size once before the allocation loop. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 26d14e6a64cc..033abb03722b 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -391,7 +391,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, { struct coda_dev *dev = ctx->dev; int width, height; - int ysize; + unsigned int ysize, ycbcr_size; int ret; int i; @@ -407,15 +407,16 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, } ysize = width * height; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ycbcr_size = round_up(ysize, 4096) + ysize / 2; + else + ycbcr_size = ysize + ysize / 2; + /* Allocate frame buffers */ for (i = 0; i < ctx->num_internal_frames; i++) { - size_t size; + size_t size = ycbcr_size; char *name; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - size = round_up(ysize, 4096) + ysize / 2; - else - size = ysize + ysize / 2; /* Add space for mvcol buffers */ if (dev->devtype->product != CODA_DX6 && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || -- cgit v1.2.3-70-g09d2 From 9e49ca530eebd6775410317aac8a68f88171f89c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 06:57:53 -0400 Subject: media: coda: streamline framebuffer size calculation a bit Remove the intermediate width and height variables, the calculation is simple enough. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 033abb03722b..bf8d8489f827 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -390,7 +390,6 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) { struct coda_dev *dev = ctx->dev; - int width, height; unsigned int ysize, ycbcr_size; int ret; int i; @@ -398,14 +397,11 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) { - width = round_up(q_data->width, 16); - height = round_up(q_data->height, 16); - } else { - width = round_up(q_data->width, 8); - height = q_data->height; - } - ysize = width * height; + ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) + ysize = round_up(q_data->width, 16) * + round_up(q_data->height, 16); + else + ysize = round_up(q_data->width, 8) * q_data->height; if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) ycbcr_size = round_up(ysize, 4096) + ysize / 2; -- cgit v1.2.3-70-g09d2 From 40fa8df63ce48d43f229d4dc6d43b3b9105abb20 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 06:57:54 -0400 Subject: media: coda: use encoder crop rectangle to set visible width and height Allow to set a crop rectangle on the encoder output queue to set the visible resolution as required by the V4L2 codec API. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 24 ++++++++--------- drivers/media/platform/coda/coda-common.c | 45 +++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 17 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index bf8d8489f827..529fc187ccfd 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -398,10 +398,10 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) - ysize = round_up(q_data->width, 16) * - round_up(q_data->height, 16); + ysize = round_up(q_data->rect.width, 16) * + round_up(q_data->rect.height, 16); else - ysize = round_up(q_data->width, 8) * q_data->height; + ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) ycbcr_size = round_up(ysize, 4096) + ysize / 2; @@ -497,8 +497,8 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->width, 16) * - DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; + size = (DIV_ROUND_UP(q_data->rect.width, 16) * + DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); if (ret < 0) @@ -630,7 +630,7 @@ static void coda_setup_iram(struct coda_ctx *ctx) struct coda_q_data *q_data_src; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - mb_width = DIV_ROUND_UP(q_data_src->width, 16); + mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); w128 = mb_width * 128; w64 = mb_width * 64; @@ -927,25 +927,25 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = 0; switch (dev->devtype->product) { case CODA_DX6: - value = (q_data_src->width & CODADX6_PICWIDTH_MASK) + value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) + value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; case CODA_HX4: case CODA_7541: if (dst_fourcc == V4L2_PIX_FMT_H264) { - value = (round_up(q_data_src->width, 16) & + value = (round_up(q_data_src->rect.width, 16) & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (round_up(q_data_src->height, 16) & + value |= (round_up(q_data_src->rect.height, 16) & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; } /* fallthrough */ case CODA_960: - value = (q_data_src->width & CODA7_PICWIDTH_MASK) + value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) + value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; } coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index f25f1417b4fa..726b3b93a486 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -933,6 +933,40 @@ static int coda_g_selection(struct file *file, void *fh, return 0; } +static int coda_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + + if (ctx->inst_type == CODA_INST_ENCODER && + s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->target == V4L2_SEL_TGT_CROP) { + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + s->r.width = clamp(s->r.width, 2U, q_data->width); + s->r.height = clamp(s->r.height, 2U, q_data->height); + + if (s->flags & V4L2_SEL_FLAG_LE) { + s->r.width = round_up(s->r.width, 2); + s->r.height = round_up(s->r.height, 2); + } else { + s->r.width = round_down(s->r.width, 2); + s->r.height = round_down(s->r.height, 2); + } + + q_data->rect = s->r; + + return 0; + } + + return coda_g_selection(file, fh, s); +} + static int coda_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) { @@ -1146,6 +1180,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = coda_g_selection, + .vidioc_s_selection = coda_s_selection, .vidioc_try_encoder_cmd = coda_try_encoder_cmd, .vidioc_encoder_cmd = coda_encoder_cmd, @@ -1587,12 +1622,12 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto out; q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if ((q_data_src->width != q_data_dst->width && - round_up(q_data_src->width, 16) != q_data_dst->width) || - (q_data_src->height != q_data_dst->height && - round_up(q_data_src->height, 16) != q_data_dst->height)) { + if ((q_data_src->rect.width != q_data_dst->width && + round_up(q_data_src->rect.width, 16) != q_data_dst->width) || + (q_data_src->rect.height != q_data_dst->height && + round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", - q_data_src->width, q_data_src->height, + q_data_src->rect.width, q_data_src->rect.height, q_data_dst->width, q_data_dst->height); ret = -EINVAL; goto err; -- cgit v1.2.3-70-g09d2 From 22fb5f0f8604a1e5b7d66637e5e85c195cc85cde Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 28 Jun 2018 07:01:47 -0400 Subject: media: coda: add missing h.264 levels This enables reordering support for h.264 main profile level 4.2, 5.0, and 5.1 streams. Even though we likely can't play back such streams at full speed, we should still recognize them correctly. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-h264.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c index 0e27412e01f5..07b4c706504f 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/coda/coda-h264.c @@ -108,6 +108,9 @@ int coda_h264_level(int level_idc) case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; default: return -EINVAL; } } -- cgit v1.2.3-70-g09d2 From acbea6798955e92eefa5fb84ecaa677a763c8762 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Thu, 28 Jun 2018 14:11:04 -0400 Subject: media: video-i2c: add hwmon support for amg88xx AMG88xx has an on-board thermistor which is used for more accurate processing of its temperature readings from the 8x8 thermopile array Cc: linux-hwmon@vger.kernel.org Signed-off-by: Matt Ranostay Acked-by: Guenter Roeck Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/video-i2c.c | 81 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5ee7a09001a7..439f6be08b95 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1033,6 +1033,7 @@ config VIDEO_I2C tristate "I2C transport video support" depends on VIDEO_V4L2 && I2C select VIDEOBUF2_VMALLOC + imply HWMON ---help--- Enable the I2C transport video support which supports the following: diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 0b347cc19aa5..7dc9338502e5 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,9 @@ struct video_i2c_chip { /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); + + /* hwmon init function */ + int (*hwmon_init)(struct video_i2c_data *data); }; static int amg88xx_xfer(struct video_i2c_data *data, char *buf) @@ -101,6 +105,74 @@ static int amg88xx_xfer(struct video_i2c_data *data, char *buf) return (ret == 2) ? 0 : -EIO; } +#if IS_ENABLED(CONFIG_HWMON) + +static const u32 amg88xx_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info amg88xx_temp = { + .type = hwmon_temp, + .config = amg88xx_temp_config, +}; + +static const struct hwmon_channel_info *amg88xx_info[] = { + &amg88xx_temp, + NULL +}; + +static umode_t amg88xx_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct video_i2c_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int tmp = i2c_smbus_read_word_data(client, 0x0e); + + if (tmp < 0) + return tmp; + + /* + * Check for sign bit, this isn't a two's complement value but an + * absolute temperature that needs to be inverted in the case of being + * negative. + */ + if (tmp & BIT(11)) + tmp = -(tmp & 0x7ff); + + *val = (tmp * 625) / 10; + + return 0; +} + +static const struct hwmon_ops amg88xx_hwmon_ops = { + .is_visible = amg88xx_is_visible, + .read = amg88xx_read, +}; + +static const struct hwmon_chip_info amg88xx_chip_info = { + .ops = &amg88xx_hwmon_ops, + .info = amg88xx_info, +}; + +static int amg88xx_hwmon_init(struct video_i2c_data *data) +{ + void *hwmon = devm_hwmon_device_register_with_info(&data->client->dev, + "amg88xx", data, &amg88xx_chip_info, NULL); + + return PTR_ERR(hwmon); +} +#else +#define amg88xx_hwmon_init NULL +#endif + #define AMG88XX 0 static const struct video_i2c_chip video_i2c_chip[] = { @@ -111,6 +183,7 @@ static const struct video_i2c_chip video_i2c_chip[] = { .buffer_size = 128, .bpp = 16, .xfer = &amg88xx_xfer, + .hwmon_init = amg88xx_hwmon_init, }, }; @@ -505,6 +578,14 @@ static int video_i2c_probe(struct i2c_client *client, video_set_drvdata(&data->vdev, data); i2c_set_clientdata(client, data); + if (data->chip->hwmon_init) { + ret = data->chip->hwmon_init(data); + if (ret < 0) { + dev_warn(&client->dev, + "failed to register hwmon device\n"); + } + } + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) goto error_unregister_device; -- cgit v1.2.3-70-g09d2 From 5a1a2f63d840dc2631505b607e11ff65ac1b7d3c Mon Sep 17 00:00:00 2001 From: Krzysztof Ha?asa Date: Thu, 28 Jun 2018 17:45:07 -0400 Subject: media: tw686x: Fix oops on buffer alloc failure The error path currently calls tw686x_video_free() which requires vc->dev to be initialized, causing a NULL dereference on uninitizalized channels. Fix this by setting the vc->dev fields for all the channels first. Fixes: f8afaa8dbc0d ("[media] tw686x: Introduce an interface to support multiple DMA modes") Signed-off-by: Krzysztof Ha?asa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 0ea8dd44026c..3a06c000f97b 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1190,6 +1190,14 @@ int tw686x_video_init(struct tw686x_dev *dev) return err; } + /* Initialize vc->dev and vc->ch for the error path */ + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + vc->dev = dev; + vc->ch = ch; + } + for (ch = 0; ch < max_channels(dev); ch++) { struct tw686x_video_channel *vc = &dev->video_channels[ch]; struct video_device *vdev; @@ -1198,9 +1206,6 @@ int tw686x_video_init(struct tw686x_dev *dev) spin_lock_init(&vc->qlock); INIT_LIST_HEAD(&vc->vidq_queued); - vc->dev = dev; - vc->ch = ch; - /* default settings */ err = tw686x_set_standard(vc, V4L2_STD_NTSC); if (err) -- cgit v1.2.3-70-g09d2 From 2e3134caf63694bf1dbb2e68829871e14d543613 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 1 Jul 2018 13:32:05 -0400 Subject: media: gspca_kinect: cast sizeof to int for comparison Comparing an int to a size, which is unsigned, causes the int to become unsigned, giving the wrong result. kinect_read returns the result of usb_control_msg, which can return a negtive error code. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ int x; expression e,e1; identifier f; @@ *x = f(...); ... when != x = e1 when != if (x < 0 || ...) { ... return ...; } *x < sizeof(e) // Signed-off-by: Julia Lawall Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/kinect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 0cfdf8a1e19d..f993f6280c56 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -163,7 +163,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, actual_len = kinect_read(udev, ibuf, 0x200); } while (actual_len == 0); gspca_dbg(gspca_dev, D_USBO, "Control reply: %d\n", actual_len); - if (actual_len < sizeof(*rhdr)) { + if (actual_len < (int)sizeof(*rhdr)) { pr_err("send_cmd: Input control transfer failed (%d)\n", actual_len); return actual_len < 0 ? actual_len : -EREMOTEIO; -- cgit v1.2.3-70-g09d2 From ea8afbabc25ade9aa34da01ffbba8085ab46ca48 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:35 -0400 Subject: media: venus: hfi_msgs: correct pointer increment Data pointer should be incremented by size of the structure not the size of a pointer, correct the mistake. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_msgs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 90c93d9603dc..589e1a6b36a9 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -60,14 +60,14 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, frame_sz = (struct hfi_framesize *)data_ptr; event.width = frame_sz->width; event.height = frame_sz->height; - data_ptr += sizeof(frame_sz); + data_ptr += sizeof(*frame_sz); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: data_ptr += sizeof(u32); profile_level = (struct hfi_profile_level *)data_ptr; event.profile = profile_level->profile; event.level = profile_level->level; - data_ptr += sizeof(profile_level); + data_ptr += sizeof(*profile_level); break; default: break; -- cgit v1.2.3-70-g09d2 From f04997bdca34221c5e953df40999c4c92edb4e0b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:36 -0400 Subject: media: venus: hfi: preparation to support venus 4xx This covers the differences between 1xx,3xx and 4xx. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 4 ++ drivers/media/platform/qcom/venus/helpers.c | 56 ++++++++++++++++-------- drivers/media/platform/qcom/venus/hfi_helper.h | 56 +++++++++++++++++++++--- drivers/media/platform/qcom/venus/hfi_venus_io.h | 7 +++ drivers/media/platform/qcom/venus/vdec.c | 5 ++- drivers/media/platform/qcom/venus/venc.c | 5 ++- 6 files changed, 104 insertions(+), 29 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 0360d295f4c8..8d3e150800c9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -305,6 +305,10 @@ struct venus_inst { struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; }; +#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) +#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX) +#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) + #define ctrl_to_inst(ctrl) \ container_of((ctrl)->handler, struct venus_inst, ctrl_handler) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 0ce9559a2924..a08ecd263e73 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -166,21 +166,38 @@ static int intbufs_unset_buffers(struct venus_inst *inst) return ret; } -static const unsigned int intbuf_types[] = { - HFI_BUFFER_INTERNAL_SCRATCH, - HFI_BUFFER_INTERNAL_SCRATCH_1, - HFI_BUFFER_INTERNAL_SCRATCH_2, +static const unsigned int intbuf_types_1xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_PERSIST, + HFI_BUFFER_INTERNAL_PERSIST_1, +}; + +static const unsigned int intbuf_types_4xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_4XX), HFI_BUFFER_INTERNAL_PERSIST, HFI_BUFFER_INTERNAL_PERSIST_1, }; static int intbufs_alloc(struct venus_inst *inst) { - unsigned int i; + const unsigned int *intbuf; + size_t arr_sz, i; int ret; - for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) { - ret = intbufs_set_buffer(inst, intbuf_types[i]); + if (IS_V4(inst->core)) { + arr_sz = ARRAY_SIZE(intbuf_types_4xx); + intbuf = intbuf_types_4xx; + } else { + arr_sz = ARRAY_SIZE(intbuf_types_1xx); + intbuf = intbuf_types_1xx; + } + + for (i = 0; i < arr_sz; i++) { + ret = intbufs_set_buffer(inst, intbuf[i]); if (ret) goto error; } @@ -257,20 +274,23 @@ static int load_scale_clocks(struct venus_core *core) set_freq: - if (core->res->hfi_version == HFI_VERSION_3XX) { - ret = clk_set_rate(clk, freq); - ret |= clk_set_rate(core->core0_clk, freq); - ret |= clk_set_rate(core->core1_clk, freq); - } else { - ret = clk_set_rate(clk, freq); - } + ret = clk_set_rate(clk, freq); + if (ret) + goto err; - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); - return ret; - } + ret = clk_set_rate(core->core0_clk, freq); + if (ret) + goto err; + + ret = clk_set_rate(core->core1_clk, freq); + if (ret) + goto err; return 0; + +err: + dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); + return ret; } static void fill_buffer_desc(const struct venus_buffer *buf, diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 55d8eb21403a..f5f1557b1176 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -121,6 +121,7 @@ #define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002 #define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e +#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000f #define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010 #define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003 @@ -376,13 +377,18 @@ #define HFI_BUFFER_OUTPUT2 0x3 #define HFI_BUFFER_INTERNAL_PERSIST 0x4 #define HFI_BUFFER_INTERNAL_PERSIST_1 0x5 -#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001 -#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002 -#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003 -#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004 -#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005 -#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006 - +#define HFI_BUFFER_INTERNAL_SCRATCH(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001) +#define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005) +#define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006) +#define HFI_BUFFER_EXTRADATA_INPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002) +#define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xa : 0x1000003) +#define HFI_BUFFER_EXTRADATA_OUTPUT2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xb : 0x1000004) #define HFI_BUFFER_TYPE_MAX 11 #define HFI_BUFFER_MODE_STATIC 0x1000001 @@ -424,12 +430,14 @@ #define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e #define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f #define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010 +#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015 /* * HFI_PROPERTY_CONFIG_COMMON_START * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000 */ #define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001 +#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002 /* * HFI_PROPERTY_PARAM_VDEC_COMMON_START @@ -438,6 +446,9 @@ #define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001 #define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002 #define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003 +#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007 +#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009 +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a /* * HFI_PROPERTY_CONFIG_VDEC_COMMON_START @@ -518,6 +529,7 @@ enum hfi_version { HFI_VERSION_1XX, HFI_VERSION_3XX, + HFI_VERSION_4XX }; struct hfi_buffer_info { @@ -767,6 +779,22 @@ struct hfi_framesize { u32 height; }; +#define VIDC_CORE_ID_DEFAULT 0 +#define VIDC_CORE_ID_1 1 +#define VIDC_CORE_ID_2 2 +#define VIDC_CORE_ID_3 3 + +struct hfi_videocores_usage_type { + u32 video_core_enable_mask; +}; + +#define VIDC_WORK_MODE_1 1 +#define VIDC_WORK_MODE_2 2 + +struct hfi_video_work_mode { + u32 video_work_mode; +}; + struct hfi_h264_vui_timing_info { u32 enable; u32 fixed_framerate; @@ -961,6 +989,12 @@ struct hfi_buffer_count_actual { u32 count_actual; }; +struct hfi_buffer_count_actual_4xx { + u32 type; + u32 count_actual; + u32 count_min_host; +}; + struct hfi_buffer_size_actual { u32 type; u32 size; @@ -971,6 +1005,14 @@ struct hfi_buffer_display_hold_count_actual { u32 hold_count; }; +/* HFI 4XX reorder the fields, use these macros */ +#define HFI_BUFREQ_HOLD_COUNT(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? 0 : (bufreq)->hold_count) +#define HFI_BUFREQ_COUNT_MIN(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->hold_count : (bufreq)->count_min) +#define HFI_BUFREQ_COUNT_MIN_HOST(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->count_min : 0) + struct hfi_buffer_requirements { u32 type; u32 size; diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index 98cc350113ab..d327b5cea334 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -110,4 +110,11 @@ #define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) #define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) +/* Venus 4xx */ +#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90) +#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94) + +#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110) +#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114) + #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index f89a91d43cc9..6da0c4e832e0 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -687,6 +687,7 @@ static int vdec_queue_setup(struct vb2_queue *q, static int vdec_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -698,14 +699,14 @@ static int vdec_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - if (inst->num_input_bufs < bufreq.count_min) + if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index f7a87a3dbb46..14bd95c52810 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -890,6 +890,7 @@ static int venc_queue_setup(struct vb2_queue *q, static int venc_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -901,7 +902,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); @@ -909,7 +910,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_input_bufs < bufreq.count_actual || - inst->num_input_bufs < bufreq.count_min) + inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; -- cgit v1.2.3-70-g09d2 From 9eb2146ecb5f1617c8a2946f7a7d02968f4c01a2 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:37 -0400 Subject: media: venus: hfi: update sequence event to handle more properties HFI version 4xx can pass more properties in the sequence change event, extend the event structure with them. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi.h | 10 ++++++ drivers/media/platform/qcom/venus/hfi_helper.h | 28 ++++++++++++++++ drivers/media/platform/qcom/venus/hfi_msgs.c | 44 ++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index 5466b7d60dd0..6038d8e0ab22 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -74,6 +74,16 @@ struct hfi_event_data { u32 tag; u32 profile; u32 level; + /* the following properties start appear from v4 onwards */ + u32 bit_depth; + u32 pic_struct; + u32 colour_space; + u32 entropy_mode; + u32 buf_count; + struct { + u32 left, top; + u32 width, height; + } input_crop; }; /* define core states */ diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index f5f1557b1176..1bc5aab1ce6b 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -801,6 +801,34 @@ struct hfi_h264_vui_timing_info { u32 time_scale; }; +struct hfi_bit_depth { + u32 buffer_type; + u32 bit_depth; +}; + +struct hfi_picture_type { + u32 is_sync_frame; + u32 picture_type; +}; + +struct hfi_pic_struct { + u32 progressive_only; +}; + +struct hfi_colour_space { + u32 colour_space; +}; + +struct hfi_extradata_input_crop { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + #define HFI_COLOR_FORMAT_MONOCHROME 0x01 #define HFI_COLOR_FORMAT_NV12 0x02 #define HFI_COLOR_FORMAT_NV21 0x03 diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 589e1a6b36a9..54cd41e5837c 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -25,10 +25,16 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_msg_event_notify_pkt *pkt) { + enum hfi_version ver = core->res->hfi_version; struct hfi_event_data event = {0}; int num_properties_changed; struct hfi_framesize *frame_sz; struct hfi_profile_level *profile_level; + struct hfi_bit_depth *pixel_depth; + struct hfi_pic_struct *pic_struct; + struct hfi_colour_space *colour_info; + struct hfi_buffer_requirements *bufreq; + struct hfi_extradata_input_crop *crop; u8 *data_ptr; u32 ptype; @@ -69,6 +75,44 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, event.level = profile_level->level; data_ptr += sizeof(*profile_level); break; + case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: + data_ptr += sizeof(u32); + pixel_depth = (struct hfi_bit_depth *)data_ptr; + event.bit_depth = pixel_depth->bit_depth; + data_ptr += sizeof(*pixel_depth); + break; + case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: + data_ptr += sizeof(u32); + pic_struct = (struct hfi_pic_struct *)data_ptr; + event.pic_struct = pic_struct->progressive_only; + data_ptr += sizeof(*pic_struct); + break; + case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: + data_ptr += sizeof(u32); + colour_info = (struct hfi_colour_space *)data_ptr; + event.colour_space = colour_info->colour_space; + data_ptr += sizeof(*colour_info); + break; + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + data_ptr += sizeof(u32); + event.entropy_mode = *(u32 *)data_ptr; + data_ptr += sizeof(u32); + break; + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + data_ptr += sizeof(u32); + bufreq = (struct hfi_buffer_requirements *)data_ptr; + event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver); + data_ptr += sizeof(*bufreq); + break; + case HFI_INDEX_EXTRADATA_INPUT_CROP: + data_ptr += sizeof(u32); + crop = (struct hfi_extradata_input_crop *)data_ptr; + event.input_crop.left = crop->left; + event.input_crop.top = crop->top; + event.input_crop.width = crop->width; + event.input_crop.height = crop->height; + data_ptr += sizeof(*crop); + break; default: break; } -- cgit v1.2.3-70-g09d2 From 6b183680dd8b9ce0aca9ad5d4dd18b5861dcf59d Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:38 -0400 Subject: media: venus: hfi_cmds: add set_properties for 4xx version Adds set_properties method to handle newer 4xx properties and fall-back to 3xx for the rest. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_cmds.c | 62 +++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 1cfeb7743041..e8389d8d8c48 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1166,6 +1166,63 @@ pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt, return ret; } +static int +pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + prop_data = &pkt->data[1]; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + pkt->data[0] = ptype; + + /* + * Any session set property which is different in 3XX packetization + * should be added as a new case below. All unchanged session set + * properties will be handled in the default case. + */ + switch (ptype) { + case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { + struct hfi_buffer_count_actual *in = pdata; + struct hfi_buffer_count_actual_4xx *count = prop_data; + + count->count_actual = in->count_actual; + count->type = in->type; + count->count_min_host = in->count_actual; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_WORK_MODE: { + struct hfi_video_work_mode *in = pdata, *wm = prop_data; + + wm->video_work_mode = in->video_work_mode; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + break; + } + case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: { + struct hfi_videocores_usage_type *in = pdata, *cu = prop_data; + + cu->video_core_enable_mask = in->video_core_enable_mask; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: + /* not implemented on Venus 4xx */ + break; + default: + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + } + + return 0; +} + int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, void *cookie, u32 ptype) { @@ -1181,7 +1238,10 @@ int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, if (hfi_ver == HFI_VERSION_1XX) return pkt_session_set_property_1x(pkt, cookie, ptype, pdata); - return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + if (hfi_ver == HFI_VERSION_3XX) + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + + return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); } void pkt_set_version(enum hfi_version version) -- cgit v1.2.3-70-g09d2 From 2074b9bed635ce96f16847d75f653f3bdb9905a6 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:39 -0400 Subject: media: venus: hfi: support session continue for 4xx version This makes possible to handle session_continue for 4xx as well. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index bca894a00c07..cbc6fad05e47 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -312,7 +312,7 @@ int hfi_session_continue(struct venus_inst *inst) { struct venus_core *core = inst->core; - if (core->res->hfi_version != HFI_VERSION_3XX) + if (core->res->hfi_version == HFI_VERSION_1XX) return 0; return core->ops->session_continue(inst); -- cgit v1.2.3-70-g09d2 From 5f43f90a99ade6db0c05e2bb45883256649c9164 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:40 -0400 Subject: media: venus: hfi: handle buffer output2 type as well This adds handling of buffers of type OUTPUT2 which is needed to support Venus 4xx version. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi.c | 3 ++- drivers/media/platform/qcom/venus/hfi_msgs.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index cbc6fad05e47..a570fdad0de0 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -473,7 +473,8 @@ int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd) if (fd->buffer_type == HFI_BUFFER_INPUT) return ops->session_etb(inst, fd); - else if (fd->buffer_type == HFI_BUFFER_OUTPUT) + else if (fd->buffer_type == HFI_BUFFER_OUTPUT || + fd->buffer_type == HFI_BUFFER_OUTPUT2) return ops->session_ftb(inst, fd); return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 54cd41e5837c..c0f3bef8299f 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -823,7 +823,8 @@ static void hfi_session_ftb_done(struct venus_core *core, error = HFI_ERR_SESSION_INVALID_PARAMETER; } - if (buffer_type != HFI_BUFFER_OUTPUT) + if (buffer_type != HFI_BUFFER_OUTPUT && + buffer_type != HFI_BUFFER_OUTPUT2) goto done; if (hfi_flags & HFI_BUFFERFLAG_EOS) -- cgit v1.2.3-70-g09d2 From eb72356e7de23d744d89e8c9408607c0fd71368c Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:41 -0400 Subject: media: venus: hfi_venus: add halt AXI support for Venus 4xx Add AXI halt support for version 4xx by using venus wrapper registers. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 18 ++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_venus_io.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 734ce11b0ed0..784b3ad1a9f6 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -532,6 +532,24 @@ static int venus_halt_axi(struct venus_hfi_device *hdev) u32 val; int ret; + if (IS_V4(hdev->core)) { + val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT); + val |= WRAPPER_CPU_AXI_HALT_HALT; + venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val); + + ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS, + val, + val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) { + dev_err(dev, "AXI bus port halt timeout\n"); + return ret; + } + + return 0; + } + /* Halt AXI and AXI IMEM VBIF Access */ val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index d327b5cea334..c0b18de1e396 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -104,7 +104,9 @@ #define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) #define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_AXI_HALT_HALT BIT(16) #define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) +#define WRAPPER_CPU_AXI_HALT_STATUS_IDLE BIT(24) #define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) #define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) -- cgit v1.2.3-70-g09d2 From bc897723467a7d56f1da81accc703429dcee5632 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:42 -0400 Subject: media: venus: hfi_venus: fix suspend function for venus 3xx versions This fixes the suspend function for Venus 3xx versions by add a check for WFI (wait for interrupt) bit. This bit is on when the ARM9 is idle and entered in low power mode. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 72 ++++++++++++++++-------- drivers/media/platform/qcom/venus/hfi_venus_io.h | 1 + 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 784b3ad1a9f6..72a8547eab39 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1444,13 +1444,40 @@ static int venus_suspend_1xx(struct venus_core *core) return 0; } +static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + return true; + + return false; +} + +static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) + return true; + + return false; +} + static int venus_suspend_3xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; - u32 ctrl_status, wfi_status; + bool val; int ret; - int cnt = 100; if (!hdev->power_enabled || hdev->suspended) return 0; @@ -1464,29 +1491,30 @@ static int venus_suspend_3xx(struct venus_core *core) return -EINVAL; } - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - - ret = venus_prepare_power_collapse(hdev, false); - if (ret) { - dev_err(dev, "prepare for power collapse fail (%d)\n", - ret); - return ret; - } + /* + * Power collapse sequence for Venus 3xx and 4xx versions: + * 1. Check for ARM9 and video core to be idle by checking WFI bit + * (bit 0) in CPU status register and by checking Idle (bit 30) in + * Control status register for video core. + * 2. Send a command to prepare for power collapse. + * 3. Check for WFI and PC_READY bits. + */ + ret = readx_poll_timeout(venus_cpu_and_video_core_idle, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; - cnt = 100; - while (cnt--) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY && - wfi_status & BIT(0)) - break; - usleep_range(1000, 1500); - } + ret = venus_prepare_power_collapse(hdev, false); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", ret); + return ret; } + ret = readx_poll_timeout(venus_cpu_idle_and_pc_ready, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; + mutex_lock(&hdev->lock); ret = venus_power_off(hdev); diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index c0b18de1e396..def0926a6dee 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -110,6 +110,7 @@ #define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) #define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) +#define WRAPPER_CPU_STATUS_WFI BIT(0) #define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) /* Venus 4xx */ -- cgit v1.2.3-70-g09d2 From 4dde81d720dcd68e439081092035a24ee20fa09c Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:43 -0400 Subject: media: venus: hfi_venus: move set of default properties to core init This moves setting of default properties (firmware debug, idle indicator and low power mode) from session init to core init. All of those properties are need to be enabled/disabled early so that they could be used before the clients are even initialized. The other reason is to set idle indicator property early before we enter into venus_suspend function where we need to check for ARM9 WFI. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 72a8547eab39..7a83e967a8ea 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1091,6 +1091,10 @@ static int venus_core_init(struct venus_core *core) if (ret) dev_warn(dev, "failed to send image version pkt to fw\n"); + ret = venus_sys_set_default_properties(hdev); + if (ret) + return ret; + return 0; } @@ -1135,10 +1139,6 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, struct hfi_session_init_pkt pkt; int ret; - ret = venus_sys_set_default_properties(hdev); - if (ret) - return ret; - ret = pkt_session_init(&pkt, inst, session_type, codec); if (ret) goto err; -- cgit v1.2.3-70-g09d2 From 17cd3d1d2e5249c58510b0c8689c45cacc201480 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:44 -0400 Subject: media: venus: hfi_venus: add suspend functionality for Venus 4xx This adds suspend (power collapse) functionality by reusing the suspend function for Venus 3xx and also enables idle indicator property for Venus 4xx (where it is disabled by default). Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 7a83e967a8ea..124085556b94 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -879,6 +879,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) if (ret) dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); + /* + * Idle indicator is disabled by default on some 4xx firmware versions, + * enable it explicitly in order to make suspend functional by checking + * WFI (wait-for-interrupt) bit. + */ + if (IS_V4(hdev->core)) + venus_sys_idle_indicator = true; + ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); if (ret) dev_warn(dev, "setting idle response ON failed (%d)\n", ret); @@ -1533,7 +1541,7 @@ static int venus_suspend_3xx(struct venus_core *core) static int venus_suspend(struct venus_core *core) { - if (core->res->hfi_version == HFI_VERSION_3XX) + if (IS_V3(core) || IS_V4(core)) return venus_suspend_3xx(core); return venus_suspend_1xx(core); -- cgit v1.2.3-70-g09d2 From aa3a8414ccea5b9412419db11f5d76ae9a4997a4 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:45 -0400 Subject: media: venus: core, helpers: add two more clocks found in Venus 4xx Add two more clocks for Venus 4xx in core structure and create a new power enable function to handle it for 3xx/4xx versions. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 4 +++ drivers/media/platform/qcom/venus/helpers.c | 51 +++++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 2 ++ drivers/media/platform/qcom/venus/vdec.c | 44 ++++++++++++++++++++----- drivers/media/platform/qcom/venus/venc.c | 44 ++++++++++++++++++++----- 5 files changed, 129 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 8d3e150800c9..2bf8839784fa 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -65,6 +65,8 @@ struct venus_format { * @clks: an array of struct clk pointers * @core0_clk: a struct clk pointer for core0 * @core1_clk: a struct clk pointer for core1 + * @core0_bus_clk: a struct clk pointer for core0 bus clock + * @core1_bus_clk: a struct clk pointer for core1 bus clock * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances * @v4l2_dev: a holder for v4l2 device structure @@ -94,6 +96,8 @@ struct venus_core { struct clk *clks[VIDC_CLKS_NUM_MAX]; struct clk *core0_clk; struct clk *core1_clk; + struct clk *core0_bus_clk; + struct clk *core1_bus_clk; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index a08ecd263e73..c8c4a4be78e3 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -13,6 +13,7 @@ * */ #include +#include #include #include #include @@ -24,6 +25,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" +#include "hfi_venus_io.h" struct intbuf { struct list_head list; @@ -786,3 +788,52 @@ void venus_helper_init_instance(struct venus_inst *inst) } } EXPORT_SYMBOL_GPL(venus_helper_init_instance); + +int venus_helper_power_enable(struct venus_core *core, u32 session_type, + bool enable) +{ + void __iomem *ctrl, *stat; + u32 val; + int ret; + + if (!IS_V3(core) && !IS_V4(core)) + return 0; + + if (IS_V3(core)) { + if (session_type == VIDC_SESSION_TYPE_DEC) + ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; + else + ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; + if (enable) + writel(0, ctrl); + else + writel(1, ctrl); + + return 0; + } + + if (session_type == VIDC_SESSION_TYPE_DEC) { + ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; + } else { + ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; + } + + if (enable) { + writel(0, ctrl); + + ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); + if (ret) + return ret; + } else { + writel(1, ctrl); + + ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_power_enable); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 971392be5df5..0e64aa95624a 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -43,4 +43,6 @@ int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +int venus_helper_power_enable(struct venus_core *core, u32 session_type, + bool enable); #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 6da0c4e832e0..0529976b4069 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1079,12 +1079,18 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + if (IS_V3(core) || IS_V4(core)) { core->core0_clk = devm_clk_get(dev, "core"); if (IS_ERR(core->core0_clk)) return PTR_ERR(core->core0_clk); } + if (IS_V4(core)) { + core->core0_bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(core->core0_bus_clk)) + return PTR_ERR(core->core0_bus_clk); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1129,15 +1135,21 @@ static int vdec_remove(struct platform_device *pdev) static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); + if (ret) + return ret; + + if (IS_V4(core)) + clk_disable_unprepare(core->core0_bus_clk); + clk_disable_unprepare(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); } static __maybe_unused int vdec_runtime_resume(struct device *dev) @@ -1145,13 +1157,29 @@ static __maybe_unused int vdec_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + + if (IS_V4(core)) + ret = clk_prepare_enable(core->core0_bus_clk); + if (ret) + goto err_unprepare_core0; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + +err_unprepare_core0: + clk_disable_unprepare(core->core0_clk); +err_power_disable: + venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 14bd95c52810..7c6be9d37d99 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1223,12 +1223,18 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + if (IS_V3(core) || IS_V4(core)) { core->core1_clk = devm_clk_get(dev, "core"); if (IS_ERR(core->core1_clk)) return PTR_ERR(core->core1_clk); } + if (IS_V4(core)) { + core->core1_bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(core->core1_bus_clk)) + return PTR_ERR(core->core1_bus_clk); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1273,15 +1279,21 @@ static int venc_remove(struct platform_device *pdev) static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); + if (ret) + return ret; + + if (IS_V4(core)) + clk_disable_unprepare(core->core1_bus_clk); + clk_disable_unprepare(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); } static __maybe_unused int venc_runtime_resume(struct device *dev) @@ -1289,13 +1301,29 @@ static __maybe_unused int venc_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + + if (IS_V4(core)) + ret = clk_prepare_enable(core->core1_bus_clk); + if (ret) + goto err_unprepare_core1; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + +err_unprepare_core1: + clk_disable_unprepare(core->core1_clk); +err_power_disable: + venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); return ret; } -- cgit v1.2.3-70-g09d2 From 1a73374a04e555103e5369429a30999114001dda Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 6 Jul 2018 08:47:58 -0400 Subject: media: venus: hfi_parser: add common capability parser This adds common capability parser for all supported Venus versions. Having it will help to enumerate better the supported raw formats and codecs and also the capabilities for every codec like max/min width/height, framerate, bitrate and so on. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/Makefile | 3 +- drivers/media/platform/qcom/venus/core.c | 85 ++++++ drivers/media/platform/qcom/venus/core.h | 74 ++--- drivers/media/platform/qcom/venus/hfi.c | 5 +- drivers/media/platform/qcom/venus/hfi_helper.h | 28 +- drivers/media/platform/qcom/venus/hfi_msgs.c | 356 ++----------------------- drivers/media/platform/qcom/venus/hfi_parser.c | 283 ++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_parser.h | 110 ++++++++ drivers/media/platform/qcom/venus/vdec.c | 38 +-- drivers/media/platform/qcom/venus/venc.c | 52 ++-- 10 files changed, 590 insertions(+), 444 deletions(-) create mode 100644 drivers/media/platform/qcom/venus/hfi_parser.c create mode 100644 drivers/media/platform/qcom/venus/hfi_parser.h (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index bfd4edf7c83f..b44b11b03e12 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -2,7 +2,8 @@ # Makefile for Qualcomm Venus driver venus-core-objs += core.o helpers.o firmware.o \ - hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o + hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ + hfi_parser.o venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 41eef376eb2d..381bfdd688db 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -152,6 +152,83 @@ static void venus_clks_disable(struct venus_core *core) clk_disable_unprepare(core->clks[i]); } +static u32 to_v4l2_codec_type(u32 codec) +{ + switch (codec) { + case HFI_VIDEO_CODEC_H264: + return V4L2_PIX_FMT_H264; + case HFI_VIDEO_CODEC_H263: + return V4L2_PIX_FMT_H263; + case HFI_VIDEO_CODEC_MPEG1: + return V4L2_PIX_FMT_MPEG1; + case HFI_VIDEO_CODEC_MPEG2: + return V4L2_PIX_FMT_MPEG2; + case HFI_VIDEO_CODEC_MPEG4: + return V4L2_PIX_FMT_MPEG4; + case HFI_VIDEO_CODEC_VC1: + return V4L2_PIX_FMT_VC1_ANNEX_G; + case HFI_VIDEO_CODEC_VP8: + return V4L2_PIX_FMT_VP8; + case HFI_VIDEO_CODEC_VP9: + return V4L2_PIX_FMT_VP9; + case HFI_VIDEO_CODEC_DIVX: + case HFI_VIDEO_CODEC_DIVX_311: + return V4L2_PIX_FMT_XVID; + default: + return 0; + } +} + +static int venus_enumerate_codecs(struct venus_core *core, u32 type) +{ + const struct hfi_inst_ops dummy_ops = {}; + struct venus_inst *inst; + u32 codec, codecs; + unsigned int i; + int ret; + + if (core->res->hfi_version != HFI_VERSION_1XX) + return 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + mutex_init(&inst->lock); + inst->core = core; + inst->session_type = type; + if (type == VIDC_SESSION_TYPE_DEC) + codecs = core->dec_codecs; + else + codecs = core->enc_codecs; + + ret = hfi_session_create(inst, &dummy_ops); + if (ret) + goto err; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + codec = (1 << i) & codecs; + if (!codec) + continue; + + ret = hfi_session_init(inst, to_v4l2_codec_type(codec)); + if (ret) + goto done; + + ret = hfi_session_deinit(inst); + if (ret) + goto done; + } + +done: + hfi_session_destroy(inst); +err: + mutex_destroy(&inst->lock); + kfree(inst); + + return ret; +} + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -219,6 +296,14 @@ static int venus_probe(struct platform_device *pdev) if (ret) goto err_venus_shutdown; + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); + if (ret) + goto err_venus_shutdown; + + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); + if (ret) + goto err_venus_shutdown; + ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) goto err_core_deinit; diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 2bf8839784fa..b995d1601c87 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -57,6 +57,30 @@ struct venus_format { u32 type; }; +#define MAX_PLANES 4 +#define MAX_FMT_ENTRIES 32 +#define MAX_CAP_ENTRIES 32 +#define MAX_ALLOC_MODE_ENTRIES 16 +#define MAX_CODEC_NUM 32 + +struct raw_formats { + u32 buftype; + u32 fmt; +}; + +struct venus_caps { + u32 codec; + u32 domain; + bool cap_bufs_mode_dynamic; + unsigned int num_caps; + struct hfi_capability caps[MAX_CAP_ENTRIES]; + unsigned int num_pl; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + unsigned int num_fmts; + struct raw_formats fmts[MAX_FMT_ENTRIES]; + bool valid; /* used only for Venus v1xx */ +}; + /** * struct venus_core - holds core parameters valid for all instances * @@ -113,8 +137,8 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; - u32 enc_codecs; - u32 dec_codecs; + unsigned long enc_codecs; + unsigned long dec_codecs; unsigned int max_sessions_supported; #define ENC_ROTATION_CAPABILITY 0x1 #define ENC_SCALING_CAPABILITY 0x2 @@ -124,6 +148,8 @@ struct venus_core { void *priv; const struct hfi_ops *ops; struct delayed_work work; + struct venus_caps caps[MAX_CODEC_NUM]; + unsigned int codecs_count; }; struct vdec_controls { @@ -216,6 +242,7 @@ struct venus_buffer { * @reconfig: a flag raised by decoder when the stream resolution changed * @reconfig_width: holds the new width * @reconfig_height: holds the new height + * @hfi_codec: current codec for this instance in HFI space * @sequence_cap: a sequence counter for capture queue * @sequence_out: a sequence counter for output queue * @m2m_dev: a reference to m2m device structure @@ -228,22 +255,8 @@ struct venus_buffer { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @cap_width: width capability - * @cap_height: height capability - * @cap_mbs_per_frame: macroblocks per frame capability - * @cap_mbs_per_sec: macroblocks per second capability - * @cap_framerate: framerate capability - * @cap_scale_x: horizontal scaling capability - * @cap_scale_y: vertical scaling capability - * @cap_bitrate: bitrate capability - * @cap_hier_p: hier capability - * @cap_ltr_count: LTR count capability - * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability * @cap_bufs_mode_static: buffers allocation mode capability * @cap_bufs_mode_dynamic: buffers allocation mode capability - * @pl_count: count of supported profiles/levels - * @pl: supported profiles/levels - * @bufreq: holds buffer requirements */ struct venus_inst { struct list_head list; @@ -280,6 +293,7 @@ struct venus_inst { bool reconfig; u32 reconfig_width; u32 reconfig_height; + u32 hfi_codec; u32 sequence_cap; u32 sequence_out; struct v4l2_m2m_dev *m2m_dev; @@ -291,22 +305,8 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; - struct hfi_capability cap_width; - struct hfi_capability cap_height; - struct hfi_capability cap_mbs_per_frame; - struct hfi_capability cap_mbs_per_sec; - struct hfi_capability cap_framerate; - struct hfi_capability cap_scale_x; - struct hfi_capability cap_scale_y; - struct hfi_capability cap_bitrate; - struct hfi_capability cap_hier_p; - struct hfi_capability cap_ltr_count; - struct hfi_capability cap_secure_output2_threshold; bool cap_bufs_mode_static; bool cap_bufs_mode_dynamic; - unsigned int pl_count; - struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; - struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) @@ -326,4 +326,18 @@ static inline void *to_hfi_priv(struct venus_core *core) return core->priv; } +static inline struct venus_caps * +venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain) +{ + unsigned int c; + + for (c = 0; c < core->codecs_count; c++) { + if (core->caps[c].codec == codec && + core->caps[c].domain == domain) + return &core->caps[c]; + } + + return NULL; +} + #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index a570fdad0de0..94ca27b0bb99 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -203,13 +203,12 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) { struct venus_core *core = inst->core; const struct hfi_ops *ops = core->ops; - u32 codec; int ret; - codec = to_codec_type(pixfmt); + inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); - ret = ops->session_init(inst, inst->session_type, codec); + ret = ops->session_init(inst, inst->session_type, inst->hfi_codec); if (ret) return ret; diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 1bc5aab1ce6b..15804ad7e65d 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -858,10 +858,23 @@ struct hfi_uncompressed_format_select { u32 format; }; +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints plane_constraints[1]; +}; + struct hfi_uncompressed_format_supported { u32 buffer_type; u32 format_entries; - u32 format_info[1]; + struct hfi_uncompressed_plane_info plane_info[1]; }; struct hfi_uncompressed_plane_actual { @@ -875,19 +888,6 @@ struct hfi_uncompressed_plane_actual_info { struct hfi_uncompressed_plane_actual plane_format[1]; }; -struct hfi_uncompressed_plane_constraints { - u32 stride_multiples; - u32 max_stride; - u32 min_plane_buffer_height_multiple; - u32 buffer_alignment; -}; - -struct hfi_uncompressed_plane_info { - u32 format; - u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_format[1]; -}; - struct hfi_uncompressed_plane_actual_constraints_info { u32 buffer_type; u32 num_planes; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index c0f3bef8299f..0ecdaa15c296 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -21,6 +21,7 @@ #include "hfi.h" #include "hfi_helper.h" #include "hfi_msgs.h" +#include "hfi_parser.h" static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_msg_event_notify_pkt *pkt) @@ -217,81 +218,28 @@ static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_sys_init_done_pkt *pkt = packet; - u32 rem_bytes, read_bytes = 0, num_properties; - u32 error, ptype; - u8 *data; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) - goto err_no_prop; - - num_properties = pkt->num_properties; + goto done; - if (!num_properties) { + if (!pkt->num_properties) { error = HFI_ERR_SYS_INVALID_PARAMETER; - goto err_no_prop; + goto done; } rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32); - - if (!rem_bytes) { + if (rem_bytes <= 0) { /* missing property data */ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - goto err_no_prop; + goto done; } - data = (u8 *)&pkt->data[0]; - - if (core->res->hfi_version == HFI_VERSION_3XX) - goto err_no_prop; - - while (num_properties && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - data += sizeof(u32); - - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: { - struct hfi_codec_supported *prop; - - prop = (struct hfi_codec_supported *)data; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - read_bytes += sizeof(*prop) + sizeof(u32); - core->dec_codecs = prop->dec_codecs; - core->enc_codecs = prop->enc_codecs; - break; - } - case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: { - struct hfi_max_sessions_supported *prop; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - prop = (struct hfi_max_sessions_supported *)data; - read_bytes += sizeof(*prop) + sizeof(u32); - core->max_sessions_supported = prop->max_sessions; - break; - } - default: - error = HFI_ERR_SYS_INVALID_PARAMETER; - break; - } - - if (error) - break; + error = hfi_parser(core, inst, pkt->data, rem_bytes); - rem_bytes -= read_bytes; - data += read_bytes; - num_properties--; - } - -err_no_prop: +done: core->error = error; complete(&core->done); } @@ -369,51 +317,6 @@ static void hfi_sys_pc_prepare_done(struct venus_core *core, dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type); } -static void -hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst) -{ - if (!in || !inst) - return; - - switch (in->capability_type) { - case HFI_CAPABILITY_FRAME_WIDTH: - inst->cap_width = *in; - break; - case HFI_CAPABILITY_FRAME_HEIGHT: - inst->cap_height = *in; - break; - case HFI_CAPABILITY_MBS_PER_FRAME: - inst->cap_mbs_per_frame = *in; - break; - case HFI_CAPABILITY_MBS_PER_SECOND: - inst->cap_mbs_per_sec = *in; - break; - case HFI_CAPABILITY_FRAMERATE: - inst->cap_framerate = *in; - break; - case HFI_CAPABILITY_SCALE_X: - inst->cap_scale_x = *in; - break; - case HFI_CAPABILITY_SCALE_Y: - inst->cap_scale_y = *in; - break; - case HFI_CAPABILITY_BITRATE: - inst->cap_bitrate = *in; - break; - case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: - inst->cap_hier_p = *in; - break; - case HFI_CAPABILITY_ENC_LTR_COUNT: - inst->cap_ltr_count = *in; - break; - case HFI_CAPABILITY_CP_OUTPUT2_THRESH: - inst->cap_secure_output2_threshold = *in; - break; - default: - break; - } -} - static unsigned int session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt, struct hfi_profile_level *profile_level) @@ -503,248 +406,27 @@ done: complete(&inst->done); } -static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst, - struct hfi_msg_session_init_done_pkt *pkt) -{ - struct device *dev = core->dev; - u32 rem_bytes, num_props; - u32 ptype, next_offset = 0; - u32 err; - u8 *data; - - rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); - if (!rem_bytes) { - dev_err(dev, "%s: missing property info\n", __func__); - return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; - } - - err = pkt->error_type; - if (err) - return err; - - data = (u8 *)&pkt->data[0]; - num_props = pkt->num_properties; - - while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - next_offset = sizeof(u32); - - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: { - struct hfi_codec_mask_supported *masks = - (struct hfi_codec_mask_supported *) - (data + next_offset); - - next_offset += sizeof(*masks); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: { - struct hfi_capabilities *caps; - struct hfi_capability *cap; - u32 num_caps; - - if ((rem_bytes - next_offset) < sizeof(*cap)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - caps = (struct hfi_capabilities *)(data + next_offset); - - num_caps = caps->num_capabilities; - cap = &caps->data[0]; - next_offset += sizeof(u32); - - while (num_caps && - (rem_bytes - next_offset) >= sizeof(u32)) { - hfi_copy_cap_prop(cap, inst); - cap++; - next_offset += sizeof(*cap); - num_caps--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: { - struct hfi_uncompressed_format_supported *prop = - (struct hfi_uncompressed_format_supported *) - (data + next_offset); - u32 num_fmt_entries; - u8 *fmt; - struct hfi_uncompressed_plane_info *inf; - - if ((rem_bytes - next_offset) < sizeof(*prop)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - num_fmt_entries = prop->format_entries; - next_offset = sizeof(*prop) - sizeof(u32); - fmt = (u8 *)&prop->format_info[0]; - - dev_dbg(dev, "uncomm format support num entries:%u\n", - num_fmt_entries); - - while (num_fmt_entries) { - struct hfi_uncompressed_plane_constraints *cnts; - u32 bytes_to_skip; - - inf = (struct hfi_uncompressed_plane_info *)fmt; - - if ((rem_bytes - next_offset) < sizeof(*inf)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - dev_dbg(dev, "plane info: fmt:%x, planes:%x\n", - inf->format, inf->num_planes); - - cnts = &inf->plane_format[0]; - dev_dbg(dev, "%u %u %u %u\n", - cnts->stride_multiples, - cnts->max_stride, - cnts->min_plane_buffer_height_multiple, - cnts->buffer_alignment); - - bytes_to_skip = sizeof(*inf) - sizeof(*cnts) + - inf->num_planes * sizeof(*cnts); - - fmt += bytes_to_skip; - next_offset += bytes_to_skip; - num_fmt_entries--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: { - struct hfi_properties_supported *prop = - (struct hfi_properties_supported *) - (data + next_offset); - - next_offset += sizeof(*prop) - sizeof(u32) - + prop->num_properties * sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: { - struct hfi_profile_level_supported *prop = - (struct hfi_profile_level_supported *) - (data + next_offset); - struct hfi_profile_level *pl; - unsigned int prop_count = 0; - unsigned int count = 0; - u8 *ptr; - - ptr = (u8 *)&prop->profile_level[0]; - prop_count = prop->profile_count; - - if (prop_count > HFI_MAX_PROFILE_COUNT) - prop_count = HFI_MAX_PROFILE_COUNT; - - while (prop_count) { - ptr++; - pl = (struct hfi_profile_level *)ptr; - - inst->pl[count].profile = pl->profile; - inst->pl[count].level = pl->level; - prop_count--; - count++; - ptr += sizeof(*pl) / sizeof(u32); - } - - inst->pl_count = count; - next_offset += sizeof(*prop) - sizeof(*pl) + - prop->profile_count * sizeof(*pl); - - num_props--; - break; - } - case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: { - next_offset += - sizeof(struct hfi_interlace_format_supported); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: { - struct hfi_nal_stream_format *nal = - (struct hfi_nal_stream_format *) - (data + next_offset); - dev_dbg(dev, "NAL format: %x\n", nal->format); - next_offset += sizeof(*nal); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: { - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: { - u32 *max_seq_sz = (u32 *)(data + next_offset); - - dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz); - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { - next_offset += sizeof(struct hfi_intra_refresh); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: { - struct hfi_buffer_alloc_mode_supported *prop = - (struct hfi_buffer_alloc_mode_supported *) - (data + next_offset); - unsigned int i; - - for (i = 0; i < prop->num_entries; i++) { - if (prop->buffer_type == HFI_BUFFER_OUTPUT || - prop->buffer_type == HFI_BUFFER_OUTPUT2) { - switch (prop->data[i]) { - case HFI_BUFFER_MODE_STATIC: - inst->cap_bufs_mode_static = true; - break; - case HFI_BUFFER_MODE_DYNAMIC: - inst->cap_bufs_mode_dynamic = true; - break; - default: - break; - } - } - } - next_offset += sizeof(*prop) - - sizeof(u32) + prop->num_entries * sizeof(u32); - num_props--; - break; - } - default: - dev_dbg(dev, "%s: default case %#x\n", __func__, ptype); - break; - } - - rem_bytes -= next_offset; - data += next_offset; - } - - return err; -} - static void hfi_session_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_session_init_done_pkt *pkt = packet; - unsigned int error; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) goto done; - if (core->res->hfi_version != HFI_VERSION_1XX) + if (!IS_V1(core)) goto done; - error = init_done_read_prop(core, inst, pkt); + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); + if (rem_bytes <= 0) { + error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; + goto done; + } + error = hfi_parser(core, inst, pkt->data, rem_bytes); done: inst->error = error; complete(&inst->done); diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c new file mode 100644 index 000000000000..afc0db3b5dc3 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linaro Ltd. + * + * Author: Stanimir Varbanov + */ +#include +#include + +#include "core.h" +#include "hfi_helper.h" +#include "hfi_parser.h" + +typedef void (*func)(struct venus_caps *cap, const void *data, + unsigned int size); + +static void init_codecs(struct venus_core *core) +{ + struct venus_caps *caps = core->caps, *cap; + unsigned long bit; + + for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_DEC; + cap->valid = false; + } + + for_each_set_bit(bit, &core->enc_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_ENC; + cap->valid = false; + } +} + +static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, + u32 codecs, u32 domain, func cb, void *data, + unsigned int size) +{ + struct venus_caps *cap; + unsigned int i; + + for (i = 0; i < caps_num; i++) { + cap = &caps[i]; + if (cap->valid && cap->domain == domain) + continue; + if (cap->codec & codecs && cap->domain == domain) + cb(cap, data, size); + } +} + +static void +fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) +{ + const u32 *type = data; + + if (*type == HFI_BUFFER_MODE_DYNAMIC) + cap->cap_bufs_mode_dynamic = true; +} + +static void +parse_alloc_mode(struct venus_core *core, struct venus_inst *inst, u32 codecs, + u32 domain, void *data) +{ + struct hfi_buffer_alloc_mode_supported *mode = data; + u32 num_entries = mode->num_entries; + u32 *type; + + if (num_entries > MAX_ALLOC_MODE_ENTRIES) + return; + + type = mode->data; + + while (num_entries--) { + if (mode->buffer_type == HFI_BUFFER_OUTPUT || + mode->buffer_type == HFI_BUFFER_OUTPUT2) { + if (*type == HFI_BUFFER_MODE_DYNAMIC && inst) + inst->cap_bufs_mode_dynamic = true; + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), + codecs, domain, fill_buf_mode, type, 1); + } + + type++; + } +} + +static void fill_profile_level(struct venus_caps *cap, const void *data, + unsigned int num) +{ + const struct hfi_profile_level *pl = data; + + memcpy(&cap->pl[cap->num_pl], pl, num * sizeof(*pl)); + cap->num_pl += num; +} + +static void +parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_profile_level_supported *pl = data; + struct hfi_profile_level *proflevel = pl->profile_level; + struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {}; + + if (pl->profile_count > HFI_MAX_PROFILE_COUNT) + return; + + memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_profile_level, pl_arr, pl->profile_count); +} + +static void +fill_caps(struct venus_caps *cap, const void *data, unsigned int num) +{ + const struct hfi_capability *caps = data; + + memcpy(&cap->caps[cap->num_caps], caps, num * sizeof(*caps)); + cap->num_caps += num; +} + +static void +parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_capabilities *caps = data; + struct hfi_capability *cap = caps->data; + u32 num_caps = caps->num_capabilities; + struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {}; + + if (num_caps > MAX_CAP_ENTRIES) + return; + + memcpy(caps_arr, cap, num_caps * sizeof(*cap)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_caps, caps_arr, num_caps); +} + +static void fill_raw_fmts(struct venus_caps *cap, const void *fmts, + unsigned int num_fmts) +{ + const struct raw_formats *formats = fmts; + + memcpy(&cap->fmts[cap->num_fmts], formats, num_fmts * sizeof(*formats)); + cap->num_fmts += num_fmts; +} + +static void +parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_uncompressed_format_supported *fmt = data; + struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info; + struct hfi_uncompressed_plane_constraints *constr; + struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; + u32 entries = fmt->format_entries; + unsigned int i = 0; + u32 num_planes; + + while (entries) { + num_planes = pinfo->num_planes; + + rawfmts[i].fmt = pinfo->format; + rawfmts[i].buftype = fmt->buffer_type; + i++; + + if (pinfo->num_planes > MAX_PLANES) + break; + + pinfo = (void *)pinfo + sizeof(*constr) * num_planes + + 2 * sizeof(u32); + entries--; + } + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_raw_fmts, rawfmts, i); +} + +static void parse_codecs(struct venus_core *core, void *data) +{ + struct hfi_codec_supported *codecs = data; + + core->dec_codecs = codecs->dec_codecs; + core->enc_codecs = codecs->enc_codecs; + + if (IS_V1(core)) { + core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; + core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + } +} + +static void parse_max_sessions(struct venus_core *core, const void *data) +{ + const struct hfi_max_sessions_supported *sessions = data; + + core->max_sessions_supported = sessions->max_sessions; +} + +static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data) +{ + struct hfi_codec_mask_supported *mask = data; + + *codecs = mask->codecs; + *domain = mask->video_domains; +} + +static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) +{ + if (!inst || !IS_V1(inst->core)) + return; + + *codecs = inst->hfi_codec; + *domain = inst->session_type; +} + +static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) +{ + struct venus_caps *caps, *cap; + unsigned int i; + u32 dom; + + if (!inst || !IS_V1(inst->core)) + return; + + caps = inst->core->caps; + dom = inst->session_type; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + cap = &caps[i]; + if (cap->codec & codecs && cap->domain == dom) + cap->valid = true; + } +} + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, + u32 size) +{ + unsigned int words_count = size >> 2; + u32 *word = buf, *data, codecs = 0, domain = 0; + + if (size % 4) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + parser_init(inst, &codecs, &domain); + + while (words_count) { + data = word + 1; + + switch (*word) { + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: + parse_codecs(core, data); + init_codecs(core); + break; + case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: + parse_max_sessions(core, data); + break; + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: + parse_codecs_mask(&codecs, &domain, data); + break; + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + parse_raw_formats(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + parse_caps(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + parse_profile_level(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: + parse_alloc_mode(core, inst, codecs, domain, data); + break; + default: + break; + } + + word++; + words_count--; + } + + parser_fini(inst, codecs, domain); + + return HFI_ERR_NONE; +} diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h new file mode 100644 index 000000000000..3e931c747e19 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Linaro Ltd. */ +#ifndef __VENUS_HFI_PARSER_H__ +#define __VENUS_HFI_PARSER_H__ + +#include "core.h" + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, + void *buf, u32 size); + +#define WHICH_CAP_MIN 0 +#define WHICH_CAP_MAX 1 +#define WHICH_CAP_STEP 2 + +static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which) +{ + struct venus_core *core = inst->core; + struct hfi_capability *cap = NULL; + struct venus_caps *caps; + unsigned int i; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return 0; + + for (i = 0; i < caps->num_caps; i++) { + if (caps->caps[i].capability_type == type) { + cap = &caps->caps[i]; + break; + } + } + + if (!cap) + return 0; + + switch (which) { + case WHICH_CAP_MIN: + return cap->min; + case WHICH_CAP_MAX: + return cap->max; + case WHICH_CAP_STEP: + return cap->step_size; + default: + break; + } + + return 0; +} + +static inline u32 cap_min(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MIN); +} + +static inline u32 cap_max(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MAX); +} + +static inline u32 cap_step(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_STEP); +} + +static inline u32 frame_width_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_height_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frate_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAMERATE); +} + +#endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 0529976b4069..b97d386b3a7a 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -24,6 +24,7 @@ #include #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "vdec.h" @@ -175,10 +176,10 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) fmt = find_format(inst, pixmp->pixelformat, f->type); } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -440,12 +441,12 @@ static int vdec_enum_framesizes(struct file *file, void *fh, fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -900,22 +901,7 @@ static void vdec_inst_init(struct venus_inst *inst) inst->fps = 30; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 30; - - inst->cap_width.min = 64; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 1; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 1; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 16; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static const struct v4l2_m2m_ops vdec_m2m_ops = { diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 7c6be9d37d99..4edca7e2cb14 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -24,6 +24,7 @@ #include #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "venc.h" @@ -299,10 +300,10 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) fmt = find_format(inst, pixmp->pixelformat, f->type); } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -551,12 +552,12 @@ static int venc_enum_framesizes(struct file *file, void *fh, if (fsize->index) return -EINVAL; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -584,18 +585,18 @@ static int venc_enum_frameintervals(struct file *file, void *fh, if (!fival->width || !fival->height) return -EINVAL; - if (fival->width > inst->cap_width.max || - fival->width < inst->cap_width.min || - fival->height > inst->cap_height.max || - fival->height < inst->cap_height.min) + if (fival->width > frame_width_max(inst) || + fival->width < frame_width_min(inst) || + fival->height > frame_height_max(inst) || + fival->height < frame_height_min(inst)) return -EINVAL; fival->stepwise.min.numerator = 1; - fival->stepwise.min.denominator = inst->cap_framerate.max; + fival->stepwise.min.denominator = frate_max(inst); fival->stepwise.max.numerator = 1; - fival->stepwise.max.denominator = inst->cap_framerate.min; + fival->stepwise.max.denominator = frate_min(inst); fival->stepwise.step.numerator = 1; - fival->stepwise.step.denominator = inst->cap_framerate.max; + fival->stepwise.step.denominator = frate_max(inst); return 0; } @@ -1089,22 +1090,7 @@ static void venc_inst_init(struct venus_inst *inst) inst->fps = 15; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 15; - - inst->cap_width.min = 96; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 2; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 2; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 24; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static int venc_open(struct file *file) -- cgit v1.2.3-70-g09d2 From f03835204f7085a18777c1419fd5b3b6217b00ad Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:47 -0400 Subject: media: venus: helpers: rename a helper function and use buffer mode from caps Rename is_reg_unreg_needed() to better name is_dynamic_bufmode() and use buffer mode from enumerated per codec capabilities. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index c8c4a4be78e3..27c4a4060c4e 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -359,18 +359,16 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } -static inline int is_reg_unreg_needed(struct venus_inst *inst) +static bool is_dynamic_bufmode(struct venus_inst *inst) { - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->core->res->hfi_version == HFI_VERSION_3XX) - return 0; + struct venus_core *core = inst->core; + struct venus_caps *caps; - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->cap_bufs_mode_dynamic && - inst->core->res->hfi_version == HFI_VERSION_1XX) + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) return 0; - return 1; + return caps->cap_bufs_mode_dynamic; } static int session_unregister_bufs(struct venus_inst *inst) @@ -379,7 +377,7 @@ static int session_unregister_bufs(struct venus_inst *inst) struct hfi_buffer_desc bd; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) { @@ -399,7 +397,7 @@ static int session_register_bufs(struct venus_inst *inst) struct venus_buffer *buf; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry(buf, &inst->registeredbufs, reg_list) { -- cgit v1.2.3-70-g09d2 From 2b0a8517fbfdbd8a2080404a5fe19ff0eb17e2e0 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:48 -0400 Subject: media: venus: helpers: add a helper function to set dynamic buffer mode Adds a new helper function to set dynamic buffer mode if it is supported by current HFI version. The dynamic buffer mode is set unconditionally for both decoder outputs. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 22 ++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 1 + drivers/media/platform/qcom/venus/vdec.c | 15 +++------------ 3 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 27c4a4060c4e..29cc84777125 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -524,6 +524,28 @@ int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) } EXPORT_SYMBOL_GPL(venus_helper_set_color_format); +int venus_helper_set_dyn_bufmode(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + struct hfi_buffer_alloc_mode mode; + int ret; + + if (!is_dynamic_bufmode(inst)) + return 0; + + mode.type = HFI_BUFFER_OUTPUT; + mode.mode = HFI_BUFFER_MODE_DYNAMIC; + + ret = hfi_session_set_property(inst, ptype, &mode); + if (ret) + return ret; + + mode.type = HFI_BUFFER_OUTPUT2; + + return hfi_session_set_property(inst, ptype, &mode); +} +EXPORT_SYMBOL_GPL(venus_helper_set_dyn_bufmode); + static void delayed_process_buf_func(struct work_struct *work) { struct venus_buffer *buf, *n; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 0e64aa95624a..52b961ed491e 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -40,6 +40,7 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); +int venus_helper_set_dyn_bufmode(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index b97d386b3a7a..ef5b14bfdde0 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -555,18 +555,9 @@ static int vdec_set_properties(struct venus_inst *inst) return ret; } - if (core->res->hfi_version == HFI_VERSION_3XX || - inst->cap_bufs_mode_dynamic) { - struct hfi_buffer_alloc_mode mode; - - ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; - mode.type = HFI_BUFFER_OUTPUT; - mode.mode = HFI_BUFFER_MODE_DYNAMIC; - - ret = hfi_session_set_property(inst, ptype, &mode); - if (ret) - return ret; - } + ret = venus_helper_set_dyn_bufmode(inst); + if (ret) + return ret; if (ctr->post_loop_deb_mode) { ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; -- cgit v1.2.3-70-g09d2 From d4a5b0a6657bab12408b792cc111bc3b7c17b39b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:49 -0400 Subject: media: venus: helpers: add helper function to set actual buffer size Add and use a helper function to set actual buffer size for particular buffer type. This is also preparation to use the second decoder output. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 12 ++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 1 + drivers/media/platform/qcom/venus/vdec.c | 10 ++-------- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 29cc84777125..6fd7458f390a 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -546,6 +546,18 @@ int venus_helper_set_dyn_bufmode(struct venus_inst *inst) } EXPORT_SYMBOL_GPL(venus_helper_set_dyn_bufmode); +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + struct hfi_buffer_size_actual bufsz; + + bufsz.type = buftype; + bufsz.size = bufsize; + + return hfi_session_set_property(inst, ptype, &bufsz); +} +EXPORT_SYMBOL_GPL(venus_helper_set_bufsize); + static void delayed_process_buf_func(struct work_struct *work) { struct venus_buffer *buf, *n; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 52b961ed491e..cd306bd8978f 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -41,6 +41,7 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); int venus_helper_set_dyn_bufmode(struct venus_inst *inst); +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index ef5b14bfdde0..b30f10b229d8 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -708,7 +708,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct venus_inst *inst = vb2_get_drv_priv(q); struct venus_core *core = inst->core; - u32 ptype; int ret; mutex_lock(&inst->lock); @@ -738,13 +737,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) goto deinit_sess; if (core->res->hfi_version == HFI_VERSION_3XX) { - struct hfi_buffer_size_actual buf_sz; - - ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; - buf_sz.type = HFI_BUFFER_OUTPUT; - buf_sz.size = inst->output_buf_size; - - ret = hfi_session_set_property(inst, ptype, &buf_sz); + ret = venus_helper_set_bufsize(inst, inst->output_buf_size, + HFI_BUFFER_OUTPUT); if (ret) goto deinit_sess; } -- cgit v1.2.3-70-g09d2 From bf26670aeae8f7f407e05f5da044e48cf9854a1b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:50 -0400 Subject: media: venus: core: delete not used buffer mode flags Delete not used flag for capture buffer allocation mode and no longer used cap_bufs_mode_dynamic from instance structure. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 4 ---- drivers/media/platform/qcom/venus/hfi_parser.c | 11 +++-------- 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index b995d1601c87..1d1a59a5d343 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -255,8 +255,6 @@ struct venus_buffer { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @cap_bufs_mode_static: buffers allocation mode capability - * @cap_bufs_mode_dynamic: buffers allocation mode capability */ struct venus_inst { struct list_head list; @@ -305,8 +303,6 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; - bool cap_bufs_mode_static; - bool cap_bufs_mode_dynamic; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index afc0db3b5dc3..2293d936e49c 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -60,8 +60,7 @@ fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) } static void -parse_alloc_mode(struct venus_core *core, struct venus_inst *inst, u32 codecs, - u32 domain, void *data) +parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_buffer_alloc_mode_supported *mode = data; u32 num_entries = mode->num_entries; @@ -74,13 +73,9 @@ parse_alloc_mode(struct venus_core *core, struct venus_inst *inst, u32 codecs, while (num_entries--) { if (mode->buffer_type == HFI_BUFFER_OUTPUT || - mode->buffer_type == HFI_BUFFER_OUTPUT2) { - if (*type == HFI_BUFFER_MODE_DYNAMIC && inst) - inst->cap_bufs_mode_dynamic = true; - + mode->buffer_type == HFI_BUFFER_OUTPUT2) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_buf_mode, type, 1); - } type++; } @@ -267,7 +262,7 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, parse_profile_level(core, codecs, domain, data); break; case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: - parse_alloc_mode(core, inst, codecs, domain, data); + parse_alloc_mode(core, codecs, domain, data); break; default: break; -- cgit v1.2.3-70-g09d2 From 404054e1777a01ac93d9767b300f6ffb41bf5972 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:51 -0400 Subject: media: venus: helpers: add buffer type argument to a helper This adds one more function argument to pass buffer type to set_output_resolution() helper function. That is a preparation to support secondary decoder output. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 5 +++-- drivers/media/platform/qcom/venus/helpers.h | 3 ++- drivers/media/platform/qcom/venus/venc.c | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 6fd7458f390a..7e764eeeb07c 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -458,12 +458,13 @@ int venus_helper_set_input_resolution(struct venus_inst *inst, EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u32 buftype) { u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE; struct hfi_framesize fs; - fs.buffer_type = HFI_BUFFER_OUTPUT; + fs.buffer_type = buftype; fs.width = width; fs.height = height; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index cd306bd8978f..0de9989adcdb 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -36,7 +36,8 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height); + unsigned int width, unsigned int height, + u32 buftype); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 4edca7e2cb14..75527db76ac1 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -793,7 +793,8 @@ static int venc_init_session(struct venus_inst *inst) goto deinit; ret = venus_helper_set_output_resolution(inst, inst->width, - inst->height); + inst->height, + HFI_BUFFER_OUTPUT); if (ret) goto deinit; -- cgit v1.2.3-70-g09d2 From ab97a3fb904d7f50855d707c89529d8d50193dcb Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:52 -0400 Subject: media: venus: helpers: add a new helper to set raw format The new helper will has one more argument for buffer type, that way the decoder can configure the format on it's secondary output. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 52 ++++++++++++++++++----------- drivers/media/platform/qcom/venus/helpers.h | 2 ++ 2 files changed, 35 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 7e764eeeb07c..4e4109e80988 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -412,6 +412,20 @@ static int session_register_bufs(struct venus_inst *inst) return ret; } +static u32 to_hfi_raw_fmt(u32 v4l2_fmt) +{ + switch (v4l2_fmt) { + case V4L2_PIX_FMT_NV12: + return HFI_COLOR_FORMAT_NV12; + case V4L2_PIX_FMT_NV21: + return HFI_COLOR_FORMAT_NV21; + default: + break; + } + + return 0; +} + int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { @@ -493,35 +507,35 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, } EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs); -int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype) { + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; struct hfi_uncompressed_format_select fmt; - u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; - int ret; + + fmt.buffer_type = buftype; + fmt.format = hfi_format; + + return hfi_session_set_property(inst, ptype, &fmt); +} +EXPORT_SYMBOL_GPL(venus_helper_set_raw_format); + +int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +{ + u32 hfi_format, buftype; if (inst->session_type == VIDC_SESSION_TYPE_DEC) - fmt.buffer_type = HFI_BUFFER_OUTPUT; + buftype = HFI_BUFFER_OUTPUT; else if (inst->session_type == VIDC_SESSION_TYPE_ENC) - fmt.buffer_type = HFI_BUFFER_INPUT; + buftype = HFI_BUFFER_INPUT; else return -EINVAL; - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - fmt.format = HFI_COLOR_FORMAT_NV12; - break; - case V4L2_PIX_FMT_NV21: - fmt.format = HFI_COLOR_FORMAT_NV21; - break; - default: + hfi_format = to_hfi_raw_fmt(pixfmt); + if (!hfi_format) return -EINVAL; - } - ret = hfi_session_set_property(inst, ptype, &fmt); - if (ret) - return ret; - - return 0; + return venus_helper_set_raw_format(inst, hfi_format, buftype); } EXPORT_SYMBOL_GPL(venus_helper_set_color_format); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 0de9989adcdb..79af7845efbd 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -40,6 +40,8 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, u32 buftype); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs); +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); int venus_helper_set_dyn_bufmode(struct venus_inst *inst); int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); -- cgit v1.2.3-70-g09d2 From 01165b8484973da047fecd082a9c67da9c988ebe Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:53 -0400 Subject: media: venus: helpers, vdec, venc: add helpers to set work mode and core usage These are new properties applicable to Venus version 4xx. Add the helpers and call them from decoder and encoder drivers. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 28 ++++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 2 ++ drivers/media/platform/qcom/venus/vdec.c | 8 ++++++++ drivers/media/platform/qcom/venus/venc.c | 8 ++++++++ 4 files changed, 46 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 4e4109e80988..3cd25e25aa70 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -486,6 +486,34 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, } EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) +{ + const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE; + struct hfi_video_work_mode wm; + + if (!IS_V4(inst->core)) + return 0; + + wm.video_work_mode = mode; + + return hfi_session_set_property(inst, ptype, &wm); +} +EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); + +int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) +{ + const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + struct hfi_videocores_usage_type cu; + + if (!IS_V4(inst->core)) + return 0; + + cu.video_core_enable_mask = usage; + + return hfi_session_set_property(inst, ptype, &cu); +} +EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); + int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs) { diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 79af7845efbd..d5e727e1ecab 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -38,6 +38,8 @@ int venus_helper_set_input_resolution(struct venus_inst *inst, int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs); int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index b30f10b229d8..99d9459e0643 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -548,6 +548,14 @@ static int vdec_set_properties(struct venus_inst *inst) u32 ptype; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + if (ret) + return ret; + + ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); + if (ret) + return ret; + if (core->res->hfi_version == HFI_VERSION_1XX) { ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; ret = hfi_session_set_property(inst, ptype, &en); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 75527db76ac1..5175786a8547 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -641,6 +641,14 @@ static int venc_set_properties(struct venus_inst *inst) u32 ptype, rate_control, bitrate, profile = 0, level = 0; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + if (ret) + return ret; + + ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2); + if (ret) + return ret; + ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; frate.buffer_type = HFI_BUFFER_OUTPUT; frate.framerate = inst->fps * (1 << 16); -- cgit v1.2.3-70-g09d2 From 1eb04b2ef4dc27f465325892bcbd19274775108e Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:54 -0400 Subject: media: venus: helpers: extend set_num_bufs helper with one more argument Extend venus_helper_set_num_bufs() helper function with one more argument to set number of output buffers for the secondary decoder output. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 16 ++++++++++++++-- drivers/media/platform/qcom/venus/helpers.h | 3 ++- drivers/media/platform/qcom/venus/vdec.c | 2 +- drivers/media/platform/qcom/venus/venc.c | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 3cd25e25aa70..13f0bc4e5ffa 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -515,7 +515,8 @@ int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, - unsigned int output_bufs) + unsigned int output_bufs, + unsigned int output2_bufs) { u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; struct hfi_buffer_count_actual buf_count; @@ -531,7 +532,18 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, buf_count.type = HFI_BUFFER_OUTPUT; buf_count.count_actual = output_bufs; - return hfi_session_set_property(inst, ptype, &buf_count); + ret = hfi_session_set_property(inst, ptype, &buf_count); + if (ret) + return ret; + + if (output2_bufs) { + buf_count.type = HFI_BUFFER_OUTPUT2; + buf_count.count_actual = output2_bufs; + + ret = hfi_session_set_property(inst, ptype, &buf_count); + } + + return ret; } EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index d5e727e1ecab..8ff4bd3ef958 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -41,7 +41,8 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, - unsigned int output_bufs); + unsigned int output_bufs, + unsigned int output2_bufs); int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, u32 buftype); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 99d9459e0643..abbdb8ac1e42 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -756,7 +756,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - VB2_MAX_FRAME); + VB2_MAX_FRAME, VB2_MAX_FRAME); if (ret) goto deinit_sess; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 5175786a8547..b1d6ac7853cf 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -961,7 +961,7 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - inst->num_output_bufs); + inst->num_output_bufs, 0); if (ret) goto deinit_sess; -- cgit v1.2.3-70-g09d2 From 130c0117e853a3561d083fd4039b630ca35ce167 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:55 -0400 Subject: media: venus: helpers: add a helper to return opb buffer sizes Add a helper function to return current output picture buffer size. OPB sizes can vary depending on the selected decoder output(s). Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 6 ++++++ drivers/media/platform/qcom/venus/helpers.c | 15 +++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 1 + 3 files changed, 22 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 1d1a59a5d343..f8e4d92ff0e1 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -239,6 +239,9 @@ struct venus_buffer { * @num_output_bufs: holds number of output buffers * @input_buf_size holds input buffer size * @output_buf_size: holds output buffer size + * @output2_buf_size: holds secondary decoder output buffer size + * @opb_buftype: output picture buffer type + * @opb_fmt: output picture buffer raw format * @reconfig: a flag raised by decoder when the stream resolution changed * @reconfig_width: holds the new width * @reconfig_height: holds the new height @@ -288,6 +291,9 @@ struct venus_inst { unsigned int num_output_bufs; unsigned int input_buf_size; unsigned int output_buf_size; + unsigned int output2_buf_size; + u32 opb_buftype; + u32 opb_fmt; bool reconfig; u32 reconfig_width; u32 reconfig_height; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 13f0bc4e5ffa..8d2f4db52e48 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -613,6 +613,21 @@ int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype) } EXPORT_SYMBOL_GPL(venus_helper_set_bufsize); +unsigned int venus_helper_get_opb_size(struct venus_inst *inst) +{ + /* the encoder has only one output */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + return inst->output_buf_size; + + if (inst->opb_buftype == HFI_BUFFER_OUTPUT) + return inst->output_buf_size; + else if (inst->opb_buftype == HFI_BUFFER_OUTPUT2) + return inst->output2_buf_size; + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_get_opb_size); + static void delayed_process_buf_func(struct work_struct *work) { struct venus_buffer *buf, *n; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 8ff4bd3ef958..92be45894a69 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -48,6 +48,7 @@ int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); int venus_helper_set_dyn_bufmode(struct venus_inst *inst); int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); +unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); -- cgit v1.2.3-70-g09d2 From 7094af54c5167990a46abd26c83891188e8934b8 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:56 -0400 Subject: media: venus: vdec: get required input buffers as well Rework and rename vdec_cap_num_buffers() to get the number of input buffers too. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 41 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index abbdb8ac1e42..927b9d97fe4d 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -601,19 +601,32 @@ deinit: return ret; } -static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num) +static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, + unsigned int *out_num) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; + *in_num = *out_num = 0; + ret = vdec_init_session(inst); if (ret) return ret; + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + goto deinit; + + *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + goto deinit; - *num = bufreq.count_actual; + *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); +deinit: hfi_session_deinit(inst); return ret; @@ -624,7 +637,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num; + unsigned int p, in_num, out_num; int ret = 0; if (*num_planes) { @@ -647,35 +660,29 @@ static int vdec_queue_setup(struct vb2_queue *q, return 0; } + ret = vdec_num_buffers(inst, &in_num, &out_num); + if (ret) + return ret; + switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; sizes[0] = get_framesize_compressed(inst->out_width, inst->out_height); inst->input_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, in_num); inst->num_input_bufs = *num_buffers; - - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - inst->num_output_bufs = num; + inst->num_output_bufs = out_num; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - *num_buffers = max(*num_buffers, num); - for (p = 0; p < *num_planes; p++) sizes[p] = get_framesize_uncompressed(p, inst->width, inst->height); - - inst->num_output_bufs = *num_buffers; inst->output_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, out_num); + inst->num_output_bufs = *num_buffers; break; default: ret = -EINVAL; -- cgit v1.2.3-70-g09d2 From ea8ce23513afec48c690b408ac06a6fd695d057a Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:57 -0400 Subject: media: venus: vdec: a new function for output configuration Make a new function vdec_output_conf() for decoder output configuration. vdec_output_conf() will set properties via HFI interface related to the output configuration, and keep vdec_set_properties() which will set properties related to decoding parameters. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 34 ++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 927b9d97fe4d..125f56e7ab1c 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -543,6 +543,22 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { static int vdec_set_properties(struct venus_inst *inst) { struct vdec_controls *ctr = &inst->controls.dec; + struct hfi_enable en = { .enable = 1 }; + u32 ptype; + int ret; + + if (ctr->post_loop_deb_mode) { + ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + + return 0; +} + +static int vdec_output_conf(struct venus_inst *inst) +{ struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; u32 ptype; @@ -567,14 +583,6 @@ static int vdec_set_properties(struct venus_inst *inst) if (ret) return ret; - if (ctr->post_loop_deb_mode) { - ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; - en.enable = 1; - ret = hfi_session_set_property(inst, ptype, &en); - if (ret) - return ret; - } - return 0; } @@ -722,7 +730,6 @@ static int vdec_verify_conf(struct venus_inst *inst) static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct venus_inst *inst = vb2_get_drv_priv(q); - struct venus_core *core = inst->core; int ret; mutex_lock(&inst->lock); @@ -751,12 +758,9 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto deinit_sess; - if (core->res->hfi_version == HFI_VERSION_3XX) { - ret = venus_helper_set_bufsize(inst, inst->output_buf_size, - HFI_BUFFER_OUTPUT); - if (ret) - goto deinit_sess; - } + ret = vdec_output_conf(inst); + if (ret) + goto deinit_sess; ret = vdec_verify_conf(inst); if (ret) -- cgit v1.2.3-70-g09d2 From e1cb72de702ad56bf0eb79264d9e95313d34b407 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:58 -0400 Subject: media: venus: helpers: move frame size calculations on common place This move the calculations of raw and compressed buffer sizes on common helper and make it identical for encoder and decoder. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 98 +++++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 2 + drivers/media/platform/qcom/venus/vdec.c | 54 ++++------------ drivers/media/platform/qcom/venus/venc.c | 56 ++++------------- 4 files changed, 126 insertions(+), 84 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 8d2f4db52e48..1e4d18448f7b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -457,6 +457,104 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, } EXPORT_SYMBOL_GPL(venus_helper_get_bufreq); +static u32 get_framesize_raw_nv12(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) +{ + u32 y_meta_stride, y_meta_plane; + u32 y_stride, y_plane; + u32 uv_meta_stride, uv_meta_plane; + u32 uv_stride, uv_plane; + u32 extradata = SZ_16K; + + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(height, 8), 16); + y_meta_plane = ALIGN(y_meta_plane, SZ_4K); + + y_stride = ALIGN(width, 128); + y_plane = ALIGN(y_stride * ALIGN(height, 32), SZ_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP(width / 2, 16), 64); + uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(height / 2, 8), 16); + uv_meta_plane = ALIGN(uv_meta_plane, SZ_4K); + + uv_stride = ALIGN(width, 128); + uv_plane = ALIGN(uv_stride * ALIGN(height / 2, 32), SZ_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane + + max(extradata, y_stride * 48), SZ_4K); +} + +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) +{ + switch (hfi_fmt) { + case HFI_COLOR_FORMAT_NV12: + case HFI_COLOR_FORMAT_NV21: + return get_framesize_raw_nv12(width, height); + case HFI_COLOR_FORMAT_NV12_UBWC: + return get_framesize_raw_nv12_ubwc(width, height); + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz_raw); + +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height) +{ + u32 hfi_fmt, sz; + bool compressed; + + switch (v4l2_fmt) { + case V4L2_PIX_FMT_MPEG: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_NO_SC: + case V4L2_PIX_FMT_H264_MVC: + case V4L2_PIX_FMT_H263: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_XVID: + case V4L2_PIX_FMT_VC1_ANNEX_G: + case V4L2_PIX_FMT_VC1_ANNEX_L: + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_VP9: + case V4L2_PIX_FMT_HEVC: + compressed = true; + break; + default: + compressed = false; + break; + } + + if (compressed) { + sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + return ALIGN(sz, SZ_4K); + } + + hfi_fmt = to_hfi_raw_fmt(v4l2_fmt); + if (!hfi_fmt) + return 0; + + return venus_helper_get_framesz_raw(hfi_fmt, width, height); +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz); + int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height) { diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 92be45894a69..92b167a47166 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,6 +33,8 @@ void venus_helper_m2m_device_run(void *priv); void venus_helper_m2m_job_abort(void *priv); int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req); +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height); +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height); int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height); int venus_helper_set_output_resolution(struct venus_inst *inst, diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 125f56e7ab1c..caed49d25fda 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -29,29 +29,6 @@ #include "helpers.h" #include "vdec.h" -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - - return ALIGN(size, SZ_4K); -} - -static u32 get_framesize_compressed(unsigned int width, unsigned int height) -{ - return ((width * height * 3 / 2) / 2) + 128; -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -160,7 +137,6 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -189,18 +165,14 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -645,7 +617,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, in_num, out_num; + unsigned int in_num, out_num; int ret = 0; if (*num_planes) { @@ -675,7 +647,8 @@ static int vdec_queue_setup(struct vb2_queue *q, switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; - sizes[0] = get_framesize_compressed(inst->out_width, + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->out_width, inst->out_height); inst->input_buf_size = sizes[0]; *num_buffers = max(*num_buffers, in_num); @@ -684,10 +657,9 @@ static int vdec_queue_setup(struct vb2_queue *q, break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; - - for (p = 0; p < *num_planes; p++) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; *num_buffers = max(*num_buffers, out_num); inst->num_output_bufs = *num_buffers; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index b1d6ac7853cf..701e5c9bc98b 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -31,32 +31,6 @@ #define NUM_B_FRAMES_MAX 4 -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - size = ALIGN(size, SZ_4K); - - return size; -} - -static u32 get_framesize_compressed(u32 width, u32 height) -{ - u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; - - return ALIGN(sz, SZ_4K); -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -284,7 +258,6 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -316,19 +289,14 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -843,7 +811,7 @@ static int venc_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num, min = 4; + unsigned int num, min = 4; int ret = 0; if (*num_planes) { @@ -878,16 +846,18 @@ static int venc_queue_setup(struct vb2_queue *q, *num_buffers = max(*num_buffers, num); inst->num_input_bufs = *num_buffers; - for (p = 0; p < *num_planes; ++p) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->width, + inst->height); inst->input_buf_size = sizes[0]; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; *num_buffers = max(*num_buffers, min); inst->num_output_bufs = *num_buffers; - sizes[0] = get_framesize_compressed(inst->width, inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; break; default: -- cgit v1.2.3-70-g09d2 From f012b23d64e9879eca987627d9b9da3d9de56746 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:03:59 -0400 Subject: media: venus: implementing multi-stream support This is implementing multi-stream decoder support. The multi-stream will be used to enable/disable the primary/secondary decoder outputs. Depending on formats on both decoder outputs we could implement downscale, dithering and supporting UBWC (universal bandwidth compression) formats. The UBWC compressed raw format is used to optimize interconnect bandwidth for bigger resolutions like 4K and hence we will get some power-saving benefits as well. Both decoder outputs are distinguished by buffer_type field in the HFI packets. For example HFI_BUFFER_OUTPUT is the buffer type for primary decoder output and HFI_BUFFER_OUTPUT2 is for secondary decoder output. Starting from Venus 4xx the DPB buffers format must be UBWC, so the multi-stream becomes mandatory for this Venus version. That means that we need to allocate internally in the driver a set of DPB buffers (with UBWC NV12 format) and give them to the firmware. The other decoder output (we called it OPB) format will be NV12 linear format and with the same resolution (or smaller in case the user wants to downscale). The DPB buffers are used for decoder reference frames and those have to be in a specific format (UBWC). So one decoder output is used to fill those reference buffers while the other output is used to fill the userspace buffers with the user requested format. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 6 + drivers/media/platform/qcom/venus/helpers.c | 198 +++++++++++++++++++++++++++- drivers/media/platform/qcom/venus/helpers.h | 6 + drivers/media/platform/qcom/venus/vdec.c | 93 ++++++++++++- drivers/media/platform/qcom/venus/venc.c | 1 + 5 files changed, 300 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index f8e4d92ff0e1..8cc49f30a363 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -215,6 +215,7 @@ struct venus_buffer { * @list: used for attach an instance to the core * @lock: instance lock * @core: a reference to the core struct + * @dpbbufs: a list of decoded picture buffers * @internalbufs: a list of internal bufferes * @registeredbufs: a list of registered capture bufferes * @delayed_process a list of delayed buffers @@ -240,6 +241,8 @@ struct venus_buffer { * @input_buf_size holds input buffer size * @output_buf_size: holds output buffer size * @output2_buf_size: holds secondary decoder output buffer size + * @dpb_buftype: decoded picture buffer type + * @dpb_fmt: decoded picture buffer raw format * @opb_buftype: output picture buffer type * @opb_fmt: output picture buffer raw format * @reconfig: a flag raised by decoder when the stream resolution changed @@ -263,6 +266,7 @@ struct venus_inst { struct list_head list; struct mutex lock; struct venus_core *core; + struct list_head dpbbufs; struct list_head internalbufs; struct list_head registeredbufs; struct list_head delayed_process; @@ -292,6 +296,8 @@ struct venus_inst { unsigned int input_buf_size; unsigned int output_buf_size; unsigned int output2_buf_size; + u32 dpb_buftype; + u32 dpb_fmt; u32 opb_buftype; u32 opb_fmt; bool reconfig; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 1e4d18448f7b..cea5b506dd51 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -85,6 +85,106 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) } EXPORT_SYMBOL_GPL(venus_helper_check_codec); +static int venus_helper_queue_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf; + int ret = 0; + + list_for_each_entry(buf, &inst->dpbbufs, list) { + struct hfi_frame_data fdata; + + memset(&fdata, 0, sizeof(fdata)); + fdata.alloc_len = buf->size; + fdata.device_addr = buf->da; + fdata.buffer_type = buf->type; + + ret = hfi_session_process_buf(inst, &fdata); + if (ret) + goto fail; + } + +fail: + return ret; +} + +int venus_helper_free_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf, *n; + + list_for_each_entry_safe(buf, n, &inst->dpbbufs, list) { + list_del_init(&buf->list); + dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da, + buf->attrs); + kfree(buf); + } + + INIT_LIST_HEAD(&inst->dpbbufs); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_free_dpb_bufs); + +int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev; + enum hfi_version ver = core->res->hfi_version; + struct hfi_buffer_requirements bufreq; + u32 buftype = inst->dpb_buftype; + unsigned int dpb_size = 0; + struct intbuf *buf; + unsigned int i; + u32 count; + int ret; + + /* no need to allocate dpb buffers */ + if (!inst->dpb_fmt) + return 0; + + if (inst->dpb_buftype == HFI_BUFFER_OUTPUT) + dpb_size = inst->output_buf_size; + else if (inst->dpb_buftype == HFI_BUFFER_OUTPUT2) + dpb_size = inst->output2_buf_size; + + if (!dpb_size) + return 0; + + ret = venus_helper_get_bufreq(inst, buftype, &bufreq); + if (ret) + return ret; + + count = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + + for (i = 0; i < count; i++) { + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + buf->type = buftype; + buf->size = dpb_size; + buf->attrs = DMA_ATTR_WRITE_COMBINE | + DMA_ATTR_NO_KERNEL_MAPPING; + buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL, + buf->attrs); + if (!buf->va) { + kfree(buf); + ret = -ENOMEM; + goto fail; + } + + list_add_tail(&buf->list, &inst->dpbbufs); + } + + return 0; + +fail: + venus_helper_free_dpb_bufs(inst); + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_alloc_dpb_bufs); + static int intbufs_set_buffer(struct venus_inst *inst, u32 type) { struct venus_core *core = inst->core; @@ -347,7 +447,10 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len) fdata.flags |= HFI_BUFFERFLAG_EOS; } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - fdata.buffer_type = HFI_BUFFER_OUTPUT; + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + fdata.buffer_type = HFI_BUFFER_OUTPUT; + else + fdata.buffer_type = inst->opb_buftype; fdata.filled_len = 0; fdata.offset = 0; } @@ -677,6 +780,27 @@ int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) } EXPORT_SYMBOL_GPL(venus_helper_set_color_format); +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en) +{ + struct hfi_multi_stream multi = {0}; + u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + int ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT; + multi.enable = out_en; + + ret = hfi_session_set_property(inst, ptype, &multi); + if (ret) + return ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT2; + multi.enable = out2_en; + + return hfi_session_set_property(inst, ptype, &multi); +} +EXPORT_SYMBOL_GPL(venus_helper_set_multistream); + int venus_helper_set_dyn_bufmode(struct venus_inst *inst) { const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; @@ -824,9 +948,10 @@ EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init); int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + unsigned int out_buf_size = venus_helper_get_opb_size(inst); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - vb2_plane_size(vb, 0) < inst->output_buf_size) + vb2_plane_size(vb, 0) < out_buf_size) return -EINVAL; if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_plane_size(vb, 0) < inst->input_buf_size) @@ -896,6 +1021,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) if (ret) hfi_session_abort(inst); + venus_helper_free_dpb_bufs(inst); + load_scale_clocks(core); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -934,8 +1061,14 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_unload_res; + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + goto err_session_stop; + return 0; +err_session_stop: + hfi_session_stop(inst); err_unload_res: hfi_session_unload_res(inst); err_unreg_bufs: @@ -989,6 +1122,67 @@ void venus_helper_init_instance(struct venus_inst *inst) } EXPORT_SYMBOL_GPL(venus_helper_init_instance); +static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt) +{ + unsigned int i; + + for (i = 0; i < caps->num_fmts; i++) { + if (caps->fmts[i].buftype == buftype && + caps->fmts[i].fmt == fmt) + return true; + } + + return false; +} + +int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, + u32 *out_fmt, u32 *out2_fmt, bool ubwc) +{ + struct venus_core *core = inst->core; + struct venus_caps *caps; + u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt); + bool found, found_ubwc; + + *out_fmt = *out2_fmt = 0; + + if (!fmt) + return -EINVAL; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return -EINVAL; + + if (ubwc) { + ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; + found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + ubwc_fmt); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + + if (found_ubwc && found) { + *out_fmt = ubwc_fmt; + *out2_fmt = fmt; + return 0; + } + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt); + if (found) { + *out_fmt = fmt; + *out2_fmt = 0; + return 0; + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + if (found) { + *out_fmt = 0; + *out2_fmt = fmt; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); + int venus_helper_power_enable(struct venus_core *core, u32 session_type, bool enable) { diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 92b167a47166..2475f284f396 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -50,10 +50,16 @@ int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); int venus_helper_set_dyn_bufmode(struct venus_inst *inst); int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en); unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, + u32 *out2_fmt, bool ubwc); +int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); +int venus_helper_free_dpb_bufs(struct venus_inst *inst); int venus_helper_power_enable(struct venus_core *core, u32 session_type, bool enable); #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index caed49d25fda..698ba4c5bce4 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -529,10 +529,16 @@ static int vdec_set_properties(struct venus_inst *inst) return 0; } +#define is_ubwc_fmt(fmt) (!!((fmt) & HFI_COLOR_FORMAT_UBWC_BASE)) + static int vdec_output_conf(struct venus_inst *inst) { struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + u32 width = inst->out_width; + u32 height = inst->out_height; + u32 out_fmt, out2_fmt; + bool ubwc = false; u32 ptype; int ret; @@ -551,6 +557,80 @@ static int vdec_output_conf(struct venus_inst *inst) return ret; } + /* Force searching UBWC formats for bigger then HD resolutions */ + if (width > 1920 && height > ALIGN(1080, 32)) + ubwc = true; + + /* For Venus v4 UBWC format is mandatory */ + if (IS_V4(core)) + ubwc = true; + + ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt, + &out2_fmt, ubwc); + if (ret) + return ret; + + inst->output_buf_size = + venus_helper_get_framesz_raw(out_fmt, width, height); + inst->output2_buf_size = + venus_helper_get_framesz_raw(out2_fmt, width, height); + + if (is_ubwc_fmt(out_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT2; + inst->opb_fmt = out2_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT; + inst->dpb_fmt = out_fmt; + } else if (is_ubwc_fmt(out2_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT2; + inst->dpb_fmt = out2_fmt; + } else { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = 0; + inst->dpb_fmt = 0; + } + + ret = venus_helper_set_raw_format(inst, inst->opb_fmt, + inst->opb_buftype); + if (ret) + return ret; + + if (inst->dpb_fmt) { + ret = venus_helper_set_multistream(inst, false, true); + if (ret) + return ret; + + ret = venus_helper_set_raw_format(inst, inst->dpb_fmt, + inst->dpb_buftype); + if (ret) + return ret; + + ret = venus_helper_set_output_resolution(inst, width, height, + HFI_BUFFER_OUTPUT2); + if (ret) + return ret; + } + + if (IS_V3(core) || IS_V4(core)) { + if (inst->output2_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output2_buf_size, + HFI_BUFFER_OUTPUT2); + if (ret) + return ret; + } + + if (inst->output_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output_buf_size, + HFI_BUFFER_OUTPUT); + if (ret) + return ret; + } + } + ret = venus_helper_set_dyn_bufmode(inst); if (ret) return ret; @@ -621,6 +701,8 @@ static int vdec_queue_setup(struct vb2_queue *q, int ret = 0; if (*num_planes) { + unsigned int output_buf_size = venus_helper_get_opb_size(inst); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && *num_planes != inst->fmt_out->num_planes) return -EINVAL; @@ -634,7 +716,7 @@ static int vdec_queue_setup(struct vb2_queue *q, return -EINVAL; if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - sizes[0] < inst->output_buf_size) + sizes[0] < output_buf_size) return -EINVAL; return 0; @@ -743,6 +825,10 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto deinit_sess; + ret = venus_helper_alloc_dpb_bufs(inst); + if (ret) + goto deinit_sess; + ret = venus_helper_vb2_start_streaming(inst); if (ret) goto deinit_sess; @@ -794,9 +880,11 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vbuf->field = V4L2_FIELD_NONE; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + unsigned int opb_sz = venus_helper_get_opb_size(inst); + vb = &vbuf->vb2_buf; vb->planes[0].bytesused = - max_t(unsigned int, inst->output_buf_size, bytesused); + max_t(unsigned int, opb_sz, bytesused); vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; @@ -934,6 +1022,7 @@ static int vdec_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 701e5c9bc98b..8fe9c1b1d211 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1082,6 +1082,7 @@ static int venc_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); -- cgit v1.2.3-70-g09d2 From 0e8954a415b862f8fb4a9f9e6689c8ea5522aa47 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:04:00 -0400 Subject: media: venus: core: add sdm845 DT compatible and resource data This adds sdm845 DT compatible string with it's resource data table. Signed-off-by: Stanimir Varbanov Reviewed-by: Rob Herring Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/qcom,venus.txt | 1 + drivers/media/platform/qcom/venus/core.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'drivers/media') diff --git a/Documentation/devicetree/bindings/media/qcom,venus.txt b/Documentation/devicetree/bindings/media/qcom,venus.txt index 2693449daf73..00d0d1bf7647 100644 --- a/Documentation/devicetree/bindings/media/qcom,venus.txt +++ b/Documentation/devicetree/bindings/media/qcom,venus.txt @@ -6,6 +6,7 @@ Definition: Value should contain one of: - "qcom,msm8916-venus" - "qcom,msm8996-venus" + - "qcom,sdm845-venus" - reg: Usage: required Value type: diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 381bfdd688db..bb6add9d340e 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -450,9 +450,31 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mdt", }; +static const struct freq_tbl sdm845_freq_table[] = { + { 1944000, 380000000 }, /* 4k UHD @ 60 */ + { 972000, 320000000 }, /* 4k UHD @ 30 */ + { 489600, 200000000 }, /* 1080p @ 60 */ + { 244800, 100000000 }, /* 1080p @ 30 */ +}; + +static const struct venus_resources sdm845_res = { + .freq_tbl = sdm845_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .max_load = 2563200, + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.2/venus.mdt", +}; + static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); -- cgit v1.2.3-70-g09d2 From 1fb9a6055561c1e77e1660aba51b1075a4a06475 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Jul 2018 09:04:01 -0400 Subject: media: venus: add HEVC codec support This add HEVC codec support for venus versions 3xx and 4xx. Signed-off-by: Stanimir Varbanov Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/helpers.c | 3 ++ drivers/media/platform/qcom/venus/hfi.c | 2 ++ drivers/media/platform/qcom/venus/vdec.c | 4 +++ drivers/media/platform/qcom/venus/venc.c | 49 +++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 8cc49f30a363..2f02365f4818 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -190,10 +190,12 @@ struct venc_controls { u32 mpeg4; u32 h264; u32 vpx; + u32 hevc; } profile; struct { u32 mpeg4; u32 h264; + u32 hevc; } level; }; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index cea5b506dd51..cd3b96e6f24b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -71,6 +71,9 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) case V4L2_PIX_FMT_XVID: codec = HFI_VIDEO_CODEC_DIVX; break; + case V4L2_PIX_FMT_HEVC: + codec = HFI_VIDEO_CODEC_HEVC; + break; default: return false; } diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 94ca27b0bb99..24207829982f 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -49,6 +49,8 @@ static u32 to_codec_type(u32 pixfmt) return HFI_VIDEO_CODEC_VP9; case V4L2_PIX_FMT_XVID: return HFI_VIDEO_CODEC_DIVX; + case V4L2_PIX_FMT_HEVC: + return HFI_VIDEO_CODEC_HEVC; default: return 0; } diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 698ba4c5bce4..0d043c33cd83 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -77,6 +77,10 @@ static const struct venus_format vdec_formats[] = { .pixfmt = V4L2_PIX_FMT_XVID, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, }, }; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 8fe9c1b1d211..20d7246a3ef9 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -59,6 +59,10 @@ static const struct venus_format venc_formats[] = { .pixfmt = V4L2_PIX_FMT_VP8, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, }, }; @@ -220,6 +224,46 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; } + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + default: + return HFI_HEVC_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return HFI_HEVC_PROFILE_MAIN_STILL_PIC; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return HFI_HEVC_PROFILE_MAIN10; + } + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + default: + return HFI_HEVC_LEVEL_1; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return HFI_HEVC_LEVEL_2; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return HFI_HEVC_LEVEL_21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return HFI_HEVC_LEVEL_3; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return HFI_HEVC_LEVEL_31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return HFI_HEVC_LEVEL_4; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return HFI_HEVC_LEVEL_41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return HFI_HEVC_LEVEL_5; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return HFI_HEVC_LEVEL_51; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + return HFI_HEVC_LEVEL_52; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: + return HFI_HEVC_LEVEL_6; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: + return HFI_HEVC_LEVEL_61; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: + return HFI_HEVC_LEVEL_62; + } } return 0; @@ -742,6 +786,11 @@ static int venc_set_properties(struct venus_inst *inst) } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { profile = 0; level = 0; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + ctr->profile.hevc); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + ctr->level.hevc); } ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; -- cgit v1.2.3-70-g09d2 From 48db0089bff6f9154f6bd98ce7a6ae3786fa8ebe Mon Sep 17 00:00:00 2001 From: Mika Båtsman Date: Wed, 16 May 2018 16:32:19 -0400 Subject: media: gl861: fix probe of dvb_usb_gl861 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probe of dvb_usb_gl861 was working at least with v4.4. Noticed the issue with v4.13 but according to similar issues the problem started with v4.9. [ 15.288065] transfer buffer not dma capable [ 15.288090] WARNING: CPU: 2 PID: 493 at drivers/usb/core/hcd.c:1595 usb_hcd_map_urb_for_dma+0x4e2/0x640 ...CUT... [ 15.288791] dvb_usb_gl861: probe of 3-7:1.0 failed with error -5 Tested with MSI Mega Sky 580 DVB-T Tuner [GL861] [mchehab+samsung@kernel.org: rebased on the top of upstream] Cc: stable@vger.kernel.org Signed-off-by: Mika Båtsman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index 9d154fdae45b..fee4b30df778 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -26,10 +26,14 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, if (wo) { req = GL861_REQ_I2C_WRITE; type = GL861_WRITE; + buf = kmemdup(wbuf, wlen, GFP_KERNEL); } else { /* rw */ req = GL861_REQ_I2C_READ; type = GL861_READ; + buf = kmalloc(rlen, GFP_KERNEL); } + if (!buf) + return -ENOMEM; switch (wlen) { case 1: @@ -42,24 +46,19 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, default: dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", KBUILD_MODNAME, wlen); + kfree(buf); return -EINVAL; } - buf = NULL; - if (rlen > 0) { - buf = kmalloc(rlen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - } + usleep_range(1000, 2000); /* avoid I2C errors */ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, buf, rlen, 2000); - if (rlen > 0) { - if (ret > 0) - memcpy(rbuf, buf, rlen); - kfree(buf); - } + if (!wo && ret > 0) + memcpy(rbuf, buf, rlen); + + kfree(buf); return ret; } -- cgit v1.2.3-70-g09d2 From 8f2240cc4e4f2ae564f9235560fca3e2115d77e0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:32 -0400 Subject: media: netup_unidvb: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c index b13e319d24b7..5f1613aec93c 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -214,11 +214,6 @@ static int netup_i2c_xfer(struct i2c_adapter *adap, struct netup_i2c *i2c = i2c_get_adapdata(adap); u16 reg; - if (num <= 0) { - dev_dbg(i2c->adap.dev.parent, - "%s(): num == %d\n", __func__, num); - return -EINVAL; - } spin_lock_irqsave(&i2c->lock, flags); if (i2c->state != STATE_DONE) { dev_dbg(i2c->adap.dev.parent, -- cgit v1.2.3-70-g09d2 From a27c0cad9e658dbc548e6731d9416b793fa68f03 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:37 -0400 Subject: media: tm6000: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c index 659b63febf85..ccd1adf862b1 100644 --- a/drivers/media/usb/tm6000/tm6000-i2c.c +++ b/drivers/media/usb/tm6000/tm6000-i2c.c @@ -145,8 +145,6 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, struct tm6000_core *dev = i2c_adap->algo_data; int addr, rc, i, byte; - if (num <= 0) - return 0; for (i = 0; i < num; i++) { addr = (msgs[i].addr << 1) & 0xff; i2c_dprintk(2, "%s %s addr=0x%x len=%d:", -- cgit v1.2.3-70-g09d2 From decb9f225efda830f2193edcce6b750b7be18150 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:38 -0400 Subject: media: dvb-usb: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 51b026fa6bfb..22554d9abd43 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -255,9 +255,6 @@ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int nu int i, j; int ret = 0; - if (!num) - return -EINVAL; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; -- cgit v1.2.3-70-g09d2 From 0eaddd7cc0ad8a6165345c08cfa62a6833c05830 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:36 -0400 Subject: media: hdpvr: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index c71ddefd2e58..5a3cb614a211 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -117,9 +117,6 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); int retval = 0, addr; - if (num <= 0) - return 0; - mutex_lock(&dev->i2c_mutex); addr = msgs[0].addr << 1; -- cgit v1.2.3-70-g09d2 From 4e4ddfd8e3123cdac71c6f97c35427cc5daeb1bf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:35 -0400 Subject: media: em28xx: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 6458682bc6e2..e19d6342e0d0 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -559,10 +559,6 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, dev->cur_i2c_bus = bus; } - if (num <= 0) { - rt_mutex_unlock(&dev->i2c_bus_lock); - return 0; - } for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; if (!msgs[i].len) { -- cgit v1.2.3-70-g09d2 From a6e65c2ce8970db5a8d0536774ccb9346e69b064 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:33 -0400 Subject: media: si4713: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si4713/radio-usb-si4713.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 05c66701a899..1ebbf0217142 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -370,9 +370,6 @@ static int si4713_transfer(struct i2c_adapter *i2c_adapter, int retval = -EINVAL; int i; - if (num <= 0) - return 0; - for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) retval = si4713_i2c_read(radio, msgs[i].buf, msgs[i].len); -- cgit v1.2.3-70-g09d2 From ada19043e5c6fd66d2b8e11c49e27c55c48f3b7c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 20 May 2018 02:50:34 -0400 Subject: media: cx231xx: don't check number of messages in the driver Since commit 1eace8344c02 ("i2c: add param sanity check to i2c_transfer()"), the I2C core does this check now. We can remove it from drivers. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 6e1bef2a45bb..15a91169e749 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -376,8 +376,6 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct cx231xx *dev = bus->dev; int addr, rc, i, byte; - if (num <= 0) - return 0; mutex_lock(&dev->i2c_lock); for (i = 0; i < num; i++) { -- cgit v1.2.3-70-g09d2 From f2047d6044e530c05a618a33d1be09b1498f9e0b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 22 May 2018 12:04:59 -0400 Subject: media: dvb-bt8xx: remove duplicate code The same code is executed regardless of whether c->frequency < 600000000 or c->frequency < 730000000 is true. This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/dvb-bt8xx.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 5ef6e2051d45..6b2a9e6eb6a8 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -386,10 +386,6 @@ static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, bs = 0x02; else if (c->frequency < 470000000) bs = 0x02; - else if (c->frequency < 600000000) - bs = 0x08; - else if (c->frequency < 730000000) - bs = 0x08; else bs = 0x08; -- cgit v1.2.3-70-g09d2 From b094e64fbd88dfcde137c2235b58c904ccac00ea Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 22 May 2018 12:20:01 -0400 Subject: media: dib0700: add code comment Add FIXME code comment: /* FIXME: check if it is fe_adap[1] */ It is likely that it should be adap->fe_adap[1].fe in the second clause, but this has never been verified. Suggested-by: Mauro Carvalho Chehab Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_devices.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index c53a969bc6be..091389fdf89e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1745,6 +1745,7 @@ static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; } else { + /* FIXME: check if it is fe_adap[1] */ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; } -- cgit v1.2.3-70-g09d2 From 64004337d0d8842b8993107c8427b80d6e36646c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 26 Jul 2018 03:11:01 -0400 Subject: media: vicodec: current -> cur 'current' is also defined in asm-generic/current.h. When compiling this driver for older kernels with the media_build system, this header is included via compat.h and it no longer compiles. Rename current to cur. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vicodec/vicodec-codec.c b/drivers/media/platform/vicodec/vicodec-codec.c index e880a6c5747f..2d047646f614 100644 --- a/drivers/media/platform/vicodec/vicodec-codec.c +++ b/drivers/media/platform/vicodec/vicodec-codec.c @@ -555,7 +555,7 @@ static int var_inter(const s16 *old, const s16 *new) return ret; } -static int decide_blocktype(const u8 *current, const u8 *reference, +static int decide_blocktype(const u8 *cur, const u8 *reference, s16 *deltablock, unsigned int stride, unsigned int input_step) { @@ -566,7 +566,7 @@ static int decide_blocktype(const u8 *current, const u8 *reference, int vari; int vard; - fill_encoder_block(current, tmp, stride, input_step); + fill_encoder_block(cur, tmp, stride, input_step); fill_encoder_block(reference, old, 8, 1); vari = var_intra(tmp); -- cgit v1.2.3-70-g09d2 From a7853c257a3ea0907467a1750ff45de4d9ba1915 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Thu, 28 Jun 2018 13:29:09 -0400 Subject: media: em28xx: Fix dual transport stream operation Addresses the following, which introduced a regression itself: Commit 509f89652f83 ("media: em28xx: fix a regression with HVR-950") The regression fix breaks dual transport stream support. Currently, when a tuner starts streaming it sets alt mode on the USB interface. The problem is, in a dual tuner model, both tuners share the same USB interface, so when the second tuner becomes active and sets alt mode on the interface it kills streaming on the other port. This patch addresses the regression by only setting alt mode on the USB interface during em28xx_start_streaming, if the device is not a dual tuner model. This allows all older and single tuner devices to explicitly set alt mode during stream startup. Testers report both isoc and bulk DualHD models work correctly with the alt mode set only once, in em28xx_dvb_init. Fixes: 509f89652f83 ("media: em28xx: fix a regression with HVR-950") Signed-off-by: Brad Love Signed-off-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index b778d8a1983e..a73faf12f7e4 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -218,7 +218,9 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(udev, dev->ifnum, dvb_alt); + if (!dev->board.has_dual_ts) + usb_set_interface(udev, dev->ifnum, dvb_alt); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; -- cgit v1.2.3-70-g09d2 From 20cdcaf903298d54b834daedf65a2ddef70cae0a Mon Sep 17 00:00:00 2001 From: Brad Love Date: Wed, 27 Jun 2018 11:32:01 -0400 Subject: media: em28xx: Fix DualHD disconnect oops During the duplication of em28xx state for the second tuner pair a pointer to alt_max_pkt_size_isoc is copied. During tear down the second tuner is destroyed first and kfrees alt_max_pkt_size_isoc, then the first tuner is destroyed and kfrees it again. The property should only be kfree'd if the tuner is PRIMARY_TS. [ 354.888560] ------------[ cut here ]------------ [ 354.888562] kernel BUG at mm/slub.c:296! [ 354.888574] invalid opcode: 0000 [#1] SMP NOPTI [ 354.888869] CPU: 1 PID: 19 Comm: kworker/1:0 Not tainted 4.18.0-rc1+ #20 [ 354.889140] Hardware name: MSI MS-7A39/B350M GAMING PRO (MS-7A39), BIOS 2.G0 04/27/2018 [ 354.889408] Workqueue: usb_hub_wq hub_event [ 354.889679] RIP: 0010:__slab_free+0x217/0x370 [ 354.889942] Code: bb c0 e8 07 41 38 c7 72 39 48 83 c4 70 5b 41 5a 41 5c 41 5d 41 5e 41 5f 5d 49 8d 62 f8 c3 f3 90 49 8b 04 24 a8 01 75 f6 eb 82 <0f> 0b 44 89 45 80 48 89 4d 88 e8 aa fa ff ff 85 c0 74 cc e9 b7 fe [ 354.890598] RSP: 0018:ffffb84c41a4fad0 EFLAGS: 00010246 [ 354.890934] RAX: ffff948646e85150 RBX: ffff948646e85150 RCX: ffff948646e85150 [ 354.891280] RDX: 00000000820001d9 RSI: fffffa8fd01ba140 RDI: ffff94865e807c00 [ 354.891649] RBP: ffffb84c41a4fb70 R08: 0000000000000001 R09: ffffffffc059ce21 [ 354.892025] R10: ffff948646e85150 R11: 0000000000000001 R12: fffffa8fd01ba140 [ 354.892403] R13: ffff948646e85150 R14: ffff94865e807c00 R15: ffff94864c92e0a0 [ 354.892780] FS: 0000000000000000(0000) GS:ffff94865ec40000(0000) knlGS:0000000000000000 [ 354.893150] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 354.893530] CR2: 00007f4e476da950 CR3: 000000040112c000 CR4: 00000000003406e0 [ 354.893917] Call Trace: [ 354.894315] ? __dev_printk+0x3c/0x80 [ 354.894695] ? _dev_info+0x64/0x80 [ 354.895082] ? em28xx_free_device+0x41/0x50 [em28xx] [ 354.895464] kfree+0x17a/0x190 [ 354.895852] ? kfree+0x17a/0x190 [ 354.896310] em28xx_free_device+0x41/0x50 [em28xx] [ 354.896698] em28xx_usb_disconnect+0xfa/0x110 [em28xx] [ 354.897083] usb_unbind_interface+0x7a/0x270 [ 354.897475] device_release_driver_internal+0x17c/0x250 [ 354.897864] device_release_driver+0x12/0x20 [ 354.898252] bus_remove_device+0xec/0x160 [ 354.898639] device_del+0x13d/0x320 [ 354.899018] ? usb_remove_ep_devs+0x1f/0x30 [ 354.899392] usb_disable_device+0x9e/0x270 [ 354.899772] usb_disconnect+0x92/0x2a0 [ 354.900149] hub_event+0x98e/0x1650 [ 354.900519] ? sched_clock_cpu+0x11/0xa0 [ 354.900890] process_one_work+0x167/0x3f0 [ 354.901251] worker_thread+0x4d/0x460 [ 354.901610] kthread+0x105/0x140 [ 354.901964] ? rescuer_thread+0x360/0x360 [ 354.902318] ? kthread_associate_blkcg+0xa0/0xa0 [ 354.902672] ret_from_fork+0x22/0x40 [ 354.903024] Modules linked in: rc_hauppauge em28xx_rc rc_core si2157 lgdt3306a i2c_mux em28xx_dvb dvb_core videobuf2_vmalloc videobuf2_memops videobuf2_common snd_hda_codec_hdmi nls_iso8859_1 edac_mce_amd kvm crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_pcm snd_seq_midi aesni_intel snd_seq_midi_event aes_x86_64 snd_rawmidi crypto_simd em28xx cryptd glue_helper asix tveeprom usbnet snd_seq v4l2_common mii videodev snd_seq_device media input_leds snd_timer joydev ccp k10temp wmi_bmof snd soundcore mac_hid sch_fq_codel parport_pc ppdev lp parport ip_tables x_tables vfio_pci vfio_virqfd irqbypass vfio_iommu_type1 vfio nouveau mxm_wmi video i2c_algo_bit ttm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops i2c_piix4 drm ahci libahci [ 354.905129] wmi gpio_amdpt gpio_generic hid_generic usbhid hid [ 354.908140] ---[ end trace c230d02716298c34 ]--- [ 354.908145] RIP: 0010:__slab_free+0x217/0x370 [ 354.908147] Code: bb c0 e8 07 41 38 c7 72 39 48 83 c4 70 5b 41 5a 41 5c 41 5d 41 5e 41 5f 5d 49 8d 62 f8 c3 f3 90 49 8b 04 24 a8 01 75 f6 eb 82 <0f> 0b 44 89 45 80 48 89 4d 88 e8 aa fa ff ff 85 c0 74 cc e9 b7 fe [ 354.908183] RSP: 0018:ffffb84c41a4fad0 EFLAGS: 00010246 [ 354.908186] RAX: ffff948646e85150 RBX: ffff948646e85150 RCX: ffff948646e85150 [ 354.908189] RDX: 00000000820001d9 RSI: fffffa8fd01ba140 RDI: ffff94865e807c00 [ 354.908191] RBP: ffffb84c41a4fb70 R08: 0000000000000001 R09: ffffffffc059ce21 [ 354.908193] R10: ffff948646e85150 R11: 0000000000000001 R12: fffffa8fd01ba140 [ 354.908195] R13: ffff948646e85150 R14: ffff94865e807c00 R15: ffff94864c92e0a0 [ 354.908198] FS: 0000000000000000(0000) GS:ffff94865ec40000(0000) knlGS:0000000000000000 [ 354.908201] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 354.908203] CR2: 00007f4e476da950 CR3: 000000016b20a000 CR4: 00000000003406e0 Signed-off-by: Brad Love Signed-off-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6c8438311d3b..ff5e41ac4723 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3376,7 +3376,9 @@ void em28xx_free_device(struct kref *ref) if (!dev->disconnected) em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size_isoc); + if (dev->ts == PRIMARY_TS) + kfree(dev->alt_max_pkt_size_isoc); + kfree(dev); } EXPORT_SYMBOL_GPL(em28xx_free_device); -- cgit v1.2.3-70-g09d2 From 5871f8bc08bc68f7d69820d40f5a0cd50c573cf6 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Wed, 27 Jun 2018 11:32:00 -0400 Subject: media: em28xx: Remove duplicate PID Half-revert: commit 5b1a270d224b ("media: dvb: add alternative USB PID for Hauppauge WinTV-soloHD")' The PID already exists on the line above. Signed-off-by: Brad Love Signed-off-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ff5e41ac4723..48bc505a872a 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2688,8 +2688,6 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */ .driver_info = EM28178_BOARD_PCTV_292E }, - { USB_DEVICE(0x2040, 0x8268), /* Hauppauge WinTV-soloHD alt. PID */ - .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x0413, 0x6f07), .driver_info = EM2861_BOARD_LEADTEK_VC100 }, { USB_DEVICE(0xeb1a, 0x8179), -- cgit v1.2.3-70-g09d2 From 0900dc319245774ce5f0abcf0484df53457c36b7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 28 Jun 2018 14:43:21 -0400 Subject: media: em28xx-cards: disable V4L2 mode for dual tuners Right now, the code that calculates alternate modes is not ready for devices with dual tuners. That's ok, as we currently don't have any such devices, but better to add a warning for such case, as, if anyone adds such device, the logic will need to be reviewed. Signed-off-by: Mauro Carvalho Chehab Acked-by: Brad Love Reviewed-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 48bc505a872a..afdc92998fff 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3861,6 +3861,17 @@ static int em28xx_usb_probe(struct usb_interface *intf, dev->has_video = false; } + if (dev->board.has_dual_ts && + (dev->tuner_type != TUNER_ABSENT || INPUT(0)->type)) { + /* + * The logic with sets alternate is not ready for dual-tuners + * which analog modes. + */ + dev_err(&intf->dev, + "We currently don't support analog TV or stream capture on dual tuners.\n"); + has_video = false; + } + /* Select USB transfer types to use */ if (has_video) { if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) -- cgit v1.2.3-70-g09d2 From ec55cdba4168d665670335e13697fc01a69f2785 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 20 Mar 2018 11:43:08 -0400 Subject: media: uvcvideo: Fix minor spelling Provide the missing 't' from straightforward. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index a36b4fb949fa..4042cbdb721b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1125,7 +1125,7 @@ done: } /* - * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Mapping V4L2 controls to UVC controls can be straightforward if done well. * Most of the UVC controls exist in V4L2, and can be mapped directly. Some * must be grouped (for instance the Red Balance, Blue Balance and Do White * Balance V4L2 controls use the White Balance Component UVC control) or -- cgit v1.2.3-70-g09d2 From 557a5c7fe6503230f6a3a41441981aed6e897d17 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2018 05:16:40 -0400 Subject: media: uvcvideo: Add KSMedia 8-bit IR format support Add support for the 8-bit IR format GUID defined in the Microsoft Kernel Streaming Media API. Reported-by: Paul Menzel Signed-off-by: Laurent Pinchart Tested-by: Paul Menzel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 5 +++++ drivers/media/usb/uvc/uvcvideo.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 8e138201330f..d46dc432456c 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -99,6 +99,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_D3DFMT_L8, .fcc = V4L2_PIX_FMT_GREY, }, + { + .name = "IR 8-bit (L8_IR)", + .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, + .fcc = V4L2_PIX_FMT_GREY, + }, { .name = "Greyscale 10-bit (Y10 )", .guid = UVC_GUID_FORMAT_Y10, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index be5cf179228b..6b955e0dd956 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -157,6 +157,9 @@ #define UVC_GUID_FORMAT_D3DFMT_L8 \ {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_KSMEDIA_L8_IR \ + {0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} /* ------------------------------------------------------------------------ -- cgit v1.2.3-70-g09d2 From 95f5cbff90b9e4324839a5c28ee3153a3c9921a5 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Mon, 4 Jun 2018 20:24:15 -0400 Subject: media: uvcvideo: Also validate buffers in BULK mode Just like for ISOC, validate the decoded BULK buffer size when possible. This avoids sending corrupted or partial buffers to userspace, which may lead to application crash or run-time failure. Signed-off-by: Nicolas Dufresne [laurent.pinchart@ideasonboard.com: Move uvc_video_validate_buffer() call to uvc_video_next_buffers()] Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a88b2e51a666..3ed5f27e786b 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1232,6 +1232,8 @@ static void uvc_video_validate_buffer(const struct uvc_streaming *stream, static void uvc_video_next_buffers(struct uvc_streaming *stream, struct uvc_buffer **video_buf, struct uvc_buffer **meta_buf) { + uvc_video_validate_buffer(stream, *video_buf); + if (*meta_buf) { struct vb2_v4l2_buffer *vb2_meta = &(*meta_buf)->buf; const struct vb2_v4l2_buffer *vb2_video = &(*video_buf)->buf; @@ -1270,10 +1272,8 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, do { ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (ret == -EAGAIN) { - uvc_video_validate_buffer(stream, buf); + if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); - } } while (ret == -EAGAIN); if (ret < 0) @@ -1289,10 +1289,8 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (buf->state == UVC_BUF_STATE_READY) { - uvc_video_validate_buffer(stream, buf); + if (buf->state == UVC_BUF_STATE_READY) uvc_video_next_buffers(stream, &buf, &meta_buf); - } } } -- cgit v1.2.3-70-g09d2 From 222964eaf2472053221341c331eb81e87c8b04f2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2018 11:07:42 -0400 Subject: media: uvcvideo: Remove a redundant check Event subscribers cannot have a NULL file handle. They are only added at a single location in the code, and the .fh pointer is used without checking there. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 4042cbdb721b..12b5be66fd2f 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1229,9 +1229,9 @@ static void uvc_ctrl_send_event(struct uvc_fh *handle, uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); list_for_each_entry(sev, &mapping->ev_subs, node) { - if (sev->fh && (sev->fh != &handle->vfh || + if (sev->fh != &handle->vfh || (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || - (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + (changes & V4L2_EVENT_CTRL_CH_FLAGS)) v4l2_event_queue_fh(sev->fh, &ev); } } -- cgit v1.2.3-70-g09d2 From 8e7a1dbc7b0ae048bf3f4b9707fe1a845e69aab7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2018 11:07:44 -0400 Subject: media: uvcvideo: Handle control pipe protocol STALLs When a command ends up in a STALL on the control pipe, use the Request Error Code control to provide a more precise error information to the user. For example, if a camera is still busy processing a control, when the same or an interrelated control set request arrives, the camera can react with a STALL and then return the "Not ready" status in response to a UVC_VC_REQUEST_ERROR_CODE_CONTROL command. With this patch the user would then get an EBUSY error code instead of a generic EPIPE. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 52 ++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 3ed5f27e786b..86a99f461fd8 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -73,17 +73,57 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, u8 intfnum, u8 cs, void *data, u16 size) { int ret; + u8 error; + u8 tmp; ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, UVC_CTRL_CONTROL_TIMEOUT); - if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on " - "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, - unit, ret, size); - return -EIO; + if (likely(ret == size)) + return 0; + + uvc_printk(KERN_ERR, + "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", + uvc_query_name(query), cs, unit, ret, size); + + if (ret != -EPIPE) + return ret; + + tmp = *(u8 *)data; + + ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum, + UVC_VC_REQUEST_ERROR_CODE_CONTROL, data, 1, + UVC_CTRL_CONTROL_TIMEOUT); + + error = *(u8 *)data; + *(u8 *)data = tmp; + + if (ret != 1) + return ret < 0 ? ret : -EPIPE; + + uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error); + + switch (error) { + case 0: + /* Cannot happen - we received a STALL */ + return -EPIPE; + case 1: /* Not ready */ + return -EBUSY; + case 2: /* Wrong state */ + return -EILSEQ; + case 3: /* Power */ + return -EREMOTE; + case 4: /* Out of range */ + return -ERANGE; + case 5: /* Invalid unit */ + case 6: /* Invalid control */ + case 7: /* Invalid Request */ + case 8: /* Invalid value within range */ + return -EINVAL; + default: /* reserved or unknown */ + break; } - return 0; + return -EPIPE; } static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, -- cgit v1.2.3-70-g09d2 From e5225c820c057537dc780244760e2e24c7d27366 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 26 Jul 2018 04:17:53 -0400 Subject: media: uvcvideo: Send a control event when a Control Change interrupt arrives UVC defines a method of handling asynchronous controls, which sends a USB packet over the interrupt pipe. This patch implements support for such packets by sending a control event to the user. Since this can involve USB traffic and, therefore, scheduling, this has to be done in a work queue. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 211 ++++++++++++++++++++++++++++--------- drivers/media/usb/uvc/uvc_status.c | 121 ++++++++++++++++++--- drivers/media/usb/uvc/uvc_v4l2.c | 4 +- drivers/media/usb/uvc/uvcvideo.h | 15 ++- include/uapi/linux/uvcvideo.h | 2 + 5 files changed, 286 insertions(+), 67 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 12b5be66fd2f..c2ad102bd693 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -971,12 +972,30 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } +static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, + const u8 *data) +{ + s32 value = mapping->get(mapping, UVC_GET_CUR, data); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + struct uvc_menu_info *menu = mapping->menu_info; + unsigned int i; + + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == value) { + value = i; + break; + } + } + } + + return value; +} + static int __uvc_ctrl_get(struct uvc_video_chain *chain, struct uvc_control *ctrl, struct uvc_control_mapping *mapping, s32 *value) { - struct uvc_menu_info *menu; - unsigned int i; int ret; if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) @@ -993,18 +1012,8 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain, ctrl->loaded = 1; } - *value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == *value) { - *value = i; - break; - } - } - } + *value = __uvc_ctrl_get_value(mapping, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); return 0; } @@ -1216,53 +1225,135 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, ev->u.ctrl.default_value = v4l2_ctrl.default_value; } -static void uvc_ctrl_send_event(struct uvc_fh *handle, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 value, u32 changes) +/* + * Send control change events to all subscribers for the @ctrl control. By + * default the subscriber that generated the event, as identified by @handle, + * is not notified unless it has set the V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK flag. + * @handle can be NULL for asynchronous events related to auto-update controls, + * in which case all subscribers are notified. + */ +static void uvc_ctrl_send_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, s32 value, u32 changes) { + struct v4l2_fh *originator = handle ? &handle->vfh : NULL; struct v4l2_subscribed_event *sev; struct v4l2_event ev; if (list_empty(&mapping->ev_subs)) return; - uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + uvc_ctrl_fill_event(chain, &ev, ctrl, mapping, value, changes); list_for_each_entry(sev, &mapping->ev_subs, node) { - if (sev->fh != &handle->vfh || + if (sev->fh != originator || (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || (changes & V4L2_EVENT_CTRL_CH_FLAGS)) v4l2_event_queue_fh(sev->fh, &ev); } } -static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, - struct uvc_control *master, u32 slave_id, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +/* + * Send control change events for the slave of the @master control identified + * by the V4L2 ID @slave_id. The @handle identifies the event subscriber that + * generated the event and may be NULL for auto-update events. + */ +static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *master, u32 slave_id) { struct uvc_control_mapping *mapping = NULL; struct uvc_control *ctrl = NULL; u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - unsigned int i; s32 val = 0; - /* - * We can skip sending an event for the slave if the slave - * is being modified in the same transaction. - */ - for (i = 0; i < xctrls_count; i++) { - if (xctrls[i].id == slave_id) - return; - } - __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); if (ctrl == NULL) return; - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0) changes |= V4L2_EVENT_CTRL_CH_VALUE; - uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); + uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_status_event_work(struct work_struct *work) +{ + struct uvc_device *dev = container_of(work, struct uvc_device, + async_ctrl.work); + struct uvc_ctrl_work *w = &dev->async_ctrl; + struct uvc_video_chain *chain = w->chain; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl = w->ctrl; + struct uvc_fh *handle; + unsigned int i; + int ret; + + mutex_lock(&chain->ctrl_mutex); + + handle = ctrl->handle; + ctrl->handle = NULL; + + list_for_each_entry(mapping, &ctrl->info.mappings, list) { + s32 value = __uvc_ctrl_get_value(mapping, w->data); + + /* + * handle may be NULL here if the device sends auto-update + * events without a prior related control set from userspace. + */ + for (i = 0; i < ARRAY_SIZE(mapping->slave_ids); ++i) { + if (!mapping->slave_ids[i]) + break; + + uvc_ctrl_send_slave_event(chain, handle, ctrl, + mapping->slave_ids[i]); + } + + uvc_ctrl_send_event(chain, handle, ctrl, mapping, value, + V4L2_EVENT_CTRL_CH_VALUE); + } + + mutex_unlock(&chain->ctrl_mutex); + + /* Resubmit the URB. */ + w->urb->interval = dev->int_ep->desc.bInterval; + ret = usb_submit_urb(w->urb, GFP_KERNEL); + if (ret < 0) + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); +} + +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data) +{ + struct uvc_device *dev = chain->dev; + struct uvc_ctrl_work *w = &dev->async_ctrl; + + if (list_empty(&ctrl->info.mappings)) { + ctrl->handle = NULL; + return false; + } + + w->data = data; + w->urb = urb; + w->chain = chain; + w->ctrl = ctrl; + + schedule_work(&w->work); + + return true; +} + +static bool uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count, u32 id) +{ + unsigned int i; + + for (i = 0; i < xctrls_count; ++i) { + if (xctrls[i].id == id) + return true; + } + + return false; } static void uvc_ctrl_send_events(struct uvc_fh *handle, @@ -1277,29 +1368,39 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, for (i = 0; i < xctrls_count; ++i) { ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + /* Notification will be sent from an Interrupt event. */ + continue; + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { - if (!mapping->slave_ids[j]) + u32 slave_id = mapping->slave_ids[j]; + + if (!slave_id) break; - uvc_ctrl_send_slave_event(handle, ctrl, - mapping->slave_ids[j], - xctrls, xctrls_count); + + /* + * We can skip sending an event for the slave if the + * slave is being modified in the same transaction. + */ + if (uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + slave_id)) + continue; + + uvc_ctrl_send_slave_event(handle->chain, handle, ctrl, + slave_id); } /* * If the master is being modified in the same transaction * flags may change too. */ - if (mapping->master_id) { - for (j = 0; j < xctrls_count; j++) { - if (xctrls[j].id == mapping->master_id) { - changes |= V4L2_EVENT_CTRL_CH_FLAGS; - break; - } - } - } + if (mapping->master_id && + uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + mapping->master_id)) + changes |= V4L2_EVENT_CTRL_CH_FLAGS; - uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, - changes); + uvc_ctrl_send_event(handle->chain, handle, ctrl, mapping, + xctrls[i].value, changes); } } @@ -1472,9 +1573,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); } -int uvc_ctrl_set(struct uvc_video_chain *chain, +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl) { + struct uvc_video_chain *chain = handle->chain; struct uvc_control *ctrl; struct uvc_control_mapping *mapping; s32 value; @@ -1581,6 +1683,9 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, mapping->set(mapping, value, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + ctrl->handle = handle; + ctrl->dirty = 1; ctrl->modified = 1; return 0; @@ -1612,7 +1717,9 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CTRL_FLAG_SET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); + UVC_CTRL_FLAG_AUTO_UPDATE : 0) + | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ? + UVC_CTRL_FLAG_ASYNCHRONOUS : 0); kfree(data); return ret; @@ -2173,6 +2280,8 @@ int uvc_ctrl_init_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work); + /* Walk the entities list and instantiate controls */ list_for_each_entry(entity, &dev->entities, list) { struct uvc_control *ctrl; @@ -2241,6 +2350,8 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + cancel_work_sync(&dev->async_ctrl.work); + /* Free controls and control mappings for all entities. */ list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) { diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 7b710410584a..0722dc684378 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -78,7 +78,24 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, /* -------------------------------------------------------------------------- * Status interrupt endpoint */ -static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) +struct uvc_streaming_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bValue[]; +} __packed; + +struct uvc_control_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bSelector; + u8 bAttribute; + u8 bValue[]; +} __packed; + +static void uvc_event_streaming(struct uvc_device *dev, + struct uvc_streaming_status *status, int len) { if (len < 3) { uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " @@ -86,31 +103,97 @@ static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) return; } - if (data[2] == 0) { + if (status->bEvent == 0) { if (len < 4) return; uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", - data[1], data[3] ? "pressed" : "released", len); - uvc_input_report_key(dev, KEY_CAMERA, data[3]); + status->bOriginator, + status->bValue[0] ? "pressed" : "released", len); + uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); } else { uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x len %d.\n", - data[1], data[2], len); + status->bOriginator, status->bEvent, len); } } -static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) +#define UVC_CTRL_VALUE_CHANGE 0 +#define UVC_CTRL_INFO_CHANGE 1 +#define UVC_CTRL_FAILURE_CHANGE 2 +#define UVC_CTRL_MIN_CHANGE 3 +#define UVC_CTRL_MAX_CHANGE 4 + +static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity, + u8 selector) { - char *attrs[3] = { "value", "info", "failure" }; + struct uvc_control *ctrl; + unsigned int i; + + for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++) + if (ctrl->info.selector == selector) + return ctrl; + + return NULL; +} - if (len < 6 || data[2] != 0 || data[4] > 2) { +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, + const struct uvc_control_status *status, + struct uvc_video_chain **chain) +{ + list_for_each_entry((*chain), &dev->chains, list) { + struct uvc_entity *entity; + struct uvc_control *ctrl; + + list_for_each_entry(entity, &(*chain)->entities, chain) { + if (entity->id != status->bOriginator) + continue; + + ctrl = uvc_event_entity_find_ctrl(entity, + status->bSelector); + if (ctrl) + return ctrl; + } + } + + return NULL; +} + +static bool uvc_event_control(struct urb *urb, + const struct uvc_control_status *status, int len) +{ + static const char *attrs[] = { "value", "info", "failure", "min", "max" }; + struct uvc_device *dev = urb->context; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + + if (len < 6 || status->bEvent != 0 || + status->bAttribute >= ARRAY_SIZE(attrs)) { uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " "received.\n"); - return; + return false; } uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", - data[1], data[3], attrs[data[4]], len); + status->bOriginator, status->bSelector, + attrs[status->bAttribute], len); + + /* Find the control. */ + ctrl = uvc_event_find_ctrl(dev, status, &chain); + if (!ctrl) + return false; + + switch (status->bAttribute) { + case UVC_CTRL_VALUE_CHANGE: + return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); + + case UVC_CTRL_INFO_CHANGE: + case UVC_CTRL_FAILURE_CHANGE: + case UVC_CTRL_MIN_CHANGE: + case UVC_CTRL_MAX_CHANGE: + break; + } + + return false; } static void uvc_status_complete(struct urb *urb) @@ -138,13 +221,23 @@ static void uvc_status_complete(struct urb *urb) len = urb->actual_length; if (len > 0) { switch (dev->status[0] & 0x0f) { - case UVC_STATUS_TYPE_CONTROL: - uvc_event_control(dev, dev->status, len); + case UVC_STATUS_TYPE_CONTROL: { + struct uvc_control_status *status = + (struct uvc_control_status *)dev->status; + + if (uvc_event_control(urb, status, len)) + /* The URB will be resubmitted in work context. */ + return; break; + } - case UVC_STATUS_TYPE_STREAMING: - uvc_event_streaming(dev, dev->status, len); + case UVC_STATUS_TYPE_STREAMING: { + struct uvc_streaming_status *status = + (struct uvc_streaming_status *)dev->status; + + uvc_event_streaming(dev, status, len); break; + } default: uvc_trace(UVC_TRACE_STATUS, "Unknown status event " diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index bd32914259ae..18a7384b50ee 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -994,7 +994,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh, if (ret < 0) return ret; - ret = uvc_ctrl_set(chain, &xctrl); + ret = uvc_ctrl_set(handle, &xctrl); if (ret < 0) { uvc_ctrl_rollback(handle); return ret; @@ -1069,7 +1069,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle, return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(chain, ctrl); + ret = uvc_ctrl_set(handle, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); ctrls->error_idx = commit ? ctrls->count : i; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6b955e0dd956..e5f5d84f1d1d 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -259,6 +260,8 @@ struct uvc_control { initialized:1; u8 *uvc_data; + + struct uvc_fh *handle; /* File handle that last changed the control. */ }; struct uvc_format_desc { @@ -603,6 +606,14 @@ struct uvc_device { u8 *status; struct input_dev *input; char input_phys[64]; + + struct uvc_ctrl_work { + struct work_struct work; + struct urb *urb; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + const void *data; + } async_ctrl; }; enum uvc_handle_state { @@ -756,6 +767,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int uvc_ctrl_init_device(struct uvc_device *dev); void uvc_ctrl_cleanup_device(struct uvc_device *dev); int uvc_ctrl_restore_values(struct uvc_device *dev); +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data); int uvc_ctrl_begin(struct uvc_video_chain *chain); int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, @@ -773,7 +786,7 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle) } int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl); int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index 020714d2c5bd..f80f05b3c423 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -28,6 +28,8 @@ #define UVC_CTRL_FLAG_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) +/* Control supports asynchronous reporting */ +#define UVC_CTRL_FLAG_ASYNCHRONOUS (1 << 8) #define UVC_CTRL_FLAG_GET_RANGE \ (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \ -- cgit v1.2.3-70-g09d2 From 3976d8d8f9b1f67ee21c5e583ec768b50605f547 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Mon, 28 May 2018 06:24:20 -0400 Subject: media: vsp1: Document vsp1_dl_body refcnt In commit 2d9445db0ee9 ("media: vsp1: Use reference counting for bodies"), a new field was introduced to the vsp1_dl_body structure to account for usage tracking of the body. Document the newly added field in the kerneldoc. Signed-off-by: Kieran Bingham Reviewed-by: Simon Horman Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index d9b9cdd8fbe2..10a24bde2299 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -43,6 +43,7 @@ struct vsp1_dl_entry { * struct vsp1_dl_body - Display list body * @list: entry in the display list list of bodies * @free: entry in the pool free body list + * @refcnt: reference tracking for the body * @pool: pool to which this body belongs * @vsp1: the VSP1 device * @entries: array of entries -- cgit v1.2.3-70-g09d2 From a00e5f074b3f3cd39d1ccdc53d4d805b014df3f3 Mon Sep 17 00:00:00 2001 From: Katsuhiro Suzuki Date: Mon, 28 May 2018 21:09:20 -0400 Subject: media: helene: fix xtal frequency setting at power on This patch fixes crystal frequency setting when power on this device. Signed-off-by: Katsuhiro Suzuki Acked-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/helene.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index a0d0b53c91d7..a5de65dcf784 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -897,7 +897,10 @@ static int helene_x_pon(struct helene_priv *priv) helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); /* 0x81 - 0x94 */ - data[0] = 0x18; /* xtal 24 MHz */ + if (priv->xtal == SONY_HELENE_XTAL_16000) + data[0] = 0x10; /* xtal 16 MHz */ + else + data[0] = 0x18; /* xtal 24 MHz */ data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ data[3] = 0x80; /* REFOUT signal output 500mVpp */ -- cgit v1.2.3-70-g09d2 From 7988b2532aca7463149eea701530d9bafc64619e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 19 Jul 2018 06:42:28 -0400 Subject: media: coda: let CODA960 firmware set frame cropping in SPS header When encoding h.264, if visible resolution is not aligned to macroblock size, frame cropping has to be set in the SPS header to produce correct streams. The CODA960 firmware can do this on its own if asked to. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 529fc187ccfd..c255c8e9db52 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -536,6 +536,8 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, { struct vb2_buffer *vb = &buf->vb2_buf; struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct v4l2_rect *r; size_t bufsize; int ret; int i; @@ -549,6 +551,23 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, if (dev->devtype->product == CODA_960) bufsize /= 1024; coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); + if (dev->devtype->product == CODA_960 && + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && + header_code == CODA_HEADER_H264_SPS) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + r = &q_data_src->rect; + + if (r->width % 16 || r->height % 16) { + u32 crop_right = round_up(r->width, 16) - r->width; + u32 crop_bottom = round_up(r->height, 16) - r->height; + + coda_write(dev, crop_right, + CODA9_CMD_ENC_HEADER_FRAME_CROP_H); + coda_write(dev, crop_bottom, + CODA9_CMD_ENC_HEADER_FRAME_CROP_V); + header_code |= CODA9_HEADER_FRAME_CROP; + } + } coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); if (ret < 0) { -- cgit v1.2.3-70-g09d2 From 0dff710d1f939c3ca42960769431cf20c095dbce Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 19 Jul 2018 06:45:10 -0400 Subject: media: coda: add SPS fixup code for frame sizes that are not multiples of 16 The CODA7541 firmware does not set the SPS frame cropping fields to properly describe coded h.264 streams with frame sizes that are not a multiple of the macroblock size. This adds RBSP parsing code and a SPS fixup routine to manually replace the cropping information in the headers produced by the firmware with the correct values. Signed-off-by: Philipp Zabel [hans.verkuil@cisco.com: added explanation of SPS RBSP to comment] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 21 +++ drivers/media/platform/coda/coda-h264.c | 316 ++++++++++++++++++++++++++++++++ drivers/media/platform/coda/coda.h | 2 + 3 files changed, 339 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index c255c8e9db52..d26c2d85a009 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1215,6 +1215,27 @@ static int coda_start_encoding(struct coda_ctx *ctx) if (ret < 0) goto out; + /* + * If visible width or height are not aligned to macroblock + * size, the crop_right and crop_bottom SPS fields must be set + * to the difference between visible and coded size. This is + * only supported by CODA960 firmware. All others do not allow + * writing frame cropping parameters, so we have to manually + * fix up the SPS RBSP (Sequence Parameter Set Raw Byte + * Sequence Payload) ourselves. + */ + if (ctx->dev->devtype->product != CODA_960 && + ((q_data_src->rect.width % 16) || + (q_data_src->rect.height % 16))) { + ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, + q_data_src->rect.height, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0], + sizeof(ctx->vpu_header[0])); + if (ret < 0) + goto out; + } + /* * Get PPS in the first frame and copy it to an * intermediate buffer. diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c index 07b4c706504f..635356a839cf 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/coda/coda-h264.c @@ -114,3 +114,319 @@ int coda_h264_level(int level_idc) default: return -EINVAL; } } + +struct rbsp { + char *buf; + int size; + int pos; +}; + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + return (rbsp->buf[ofs] >> shift) & 1; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->buf[ofs] &= ~(1 << shift); + rbsp->buf[ofs] |= bit << shift; + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) +{ + int i, ret; + int tmp = 0; + + if (num > 32) + return -EINVAL; + + for (i = 0; i < num; i++) { + ret = rbsp_read_bit(rbsp); + if (ret < 0) + return ret; + tmp |= ret << (num - i - 1); + } + + if (val) + *val = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) +{ + int ret; + + while (num--) { + ret = rbsp_write_bit(rbsp, (value >> num) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (val) + *val = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) +{ + int i; + int ret; + int tmp = value + 1; + int leading_zero_bits = fls(tmp) - 1; + + for (i = 0; i < leading_zero_bits; i++) { + ret = rbsp_write_bit(rbsp, 0); + if (ret) + return ret; + } + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *val) +{ + unsigned int tmp; + int ret; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (val) { + if (tmp & 1) + *val = (tmp + 1) / 2; + else + *val = -(tmp / 2); + } + + return 0; +} + +/** + * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS + * @ctx: encoder context + * @width: visible width + * @height: visible height + * @buf: buffer containing h.264 SPS RBSP, starting with NAL header + * @size: modified RBSP size return value + * @max_size: available size in buf + * + * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the + * given visible width and height. + */ +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size) +{ + int profile_idc; + unsigned int pic_order_cnt_type; + int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; + int frame_mbs_only_flag, frame_cropping_flag; + int vui_parameters_present_flag; + unsigned int crop_right, crop_bottom; + struct rbsp sps; + int pos; + int ret; + + if (*size < 8 || *size >= max_size) + return -EINVAL; + + sps.buf = buf + 5; /* Skip NAL header */ + sps.size = *size - 5; + + profile_idc = sps.buf[0]; + /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ + /* Skip level_idc */ + sps.pos = 24; + + /* seq_parameter_set_id */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || + profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || + profile_idc == 135) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling profile_idc %d not implemented\n", + __func__, profile_idc); + return -EINVAL; + } + + /* log2_max_frame_num_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, &pic_order_cnt_type); + if (ret) + return ret; + + if (pic_order_cnt_type == 0) { + /* log2_max_pic_order_cnt_lsb_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + } else if (pic_order_cnt_type == 1) { + unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; + + /* delta_pic_order_always_zero_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + /* offset_for_non_ref_pic */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + /* offset_for_top_to_bottom_field */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, + &num_ref_frames_in_pic_order_cnt_cycle); + if (ret) + return ret; + for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* offset_for_ref_frame */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + } + } + + /* max_num_ref_frames */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + /* gaps_in_frame_num_value_allowed_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); + if (ret) + return ret; + frame_mbs_only_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (!frame_mbs_only_flag) { + /* mb_adaptive_frame_field_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + } + /* direct_8x8_inference_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + + /* Mark position of the frame cropping flag */ + pos = sps.pos; + frame_cropping_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (frame_cropping_flag) { + unsigned int crop_left, crop_top; + + ret = rbsp_read_uev(&sps, &crop_left); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_right); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_top); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_bottom); + if (ret) + return ret; + } + vui_parameters_present_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (vui_parameters_present_flag) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling vui_parameters not implemented\n", + __func__); + return -EINVAL; + } + + crop_right = round_up(width, 16) - width; + crop_bottom = round_up(height, 16) - height; + crop_right /= 2; + if (frame_mbs_only_flag) + crop_bottom /= 2; + else + crop_bottom /= 4; + + + sps.size = max_size - 5; + sps.pos = pos; + frame_cropping_flag = 1; + ret = rbsp_write_bit(&sps, frame_cropping_flag); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_left */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_right); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_top */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_bottom); + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 1); + if (ret) + return ret; + + *size = 5 + DIV_ROUND_UP(sps.pos, 8); + + return 0; +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 9f57f73161d6..19ac0b9dc6eb 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -305,6 +305,8 @@ int coda_h264_padding(int size, char *p); int coda_h264_profile(int profile_idc); int coda_h264_level(int level_idc); int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size); bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); -- cgit v1.2.3-70-g09d2 From 43bd1dce39cea87a75e8691aad4ce11a9b457d8e Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Fri, 20 Jul 2018 10:09:16 -0400 Subject: media: vivid: Fix V4L2_FIELD_ALTERNATE new frame check The vivid driver will overlay stream time on generated frames. Though, in interlacing mode V4L2_FIELD_ALTERNATE, each field is separate and must have the same time to ensure proper render. Though, this time was only updated every 2 frames as the code was checking against the wrong counter (frame counter rather then field counter). Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-kthread-cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 3fdb280c36ca..f06003bb8e42 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -477,7 +477,7 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) /* Updates stream time, only update at the start of a new frame. */ if (dev->field_cap != V4L2_FIELD_ALTERNATE || - (buf->vb.sequence & 1) == 0) + (dev->vid_cap_seq_count & 1) == 0) dev->ms_vid_cap = jiffies_to_msecs(jiffies - dev->jiffies_vid_cap); -- cgit v1.2.3-70-g09d2 From 87e1a881e1fe3bfd89903c54ae0d6d6adedb76f1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 25 Jul 2018 08:51:39 -0400 Subject: media: media.h: remove linux/version.h include The media.h public header is one of only three public headers that include linux/version.h. Drop it from media.h. It was only used for an obsolete define. It has to be added to media-device.c, since that source relied on media.h to include it. Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 1 + include/uapi/linux/media.h | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 14959b19a342..fcdf3d5dc4b6 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 82ec9f132a53..36f76e777ef9 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -25,7 +25,6 @@ #endif #include #include -#include struct media_device_info { char driver[16]; @@ -421,7 +420,7 @@ struct media_v2_topology { #define MEDIA_INTF_T_ALSA_TIMER (MEDIA_INTF_T_ALSA_BASE + 7) /* Obsolete symbol for media_version, no longer used in the kernel */ -#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) +#define MEDIA_API_VERSION ((0 << 16) | (1 << 8) | 0) #endif -- cgit v1.2.3-70-g09d2 From ddc5c1f799123224a8e02c49d992f962bed225f3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 25 Jul 2018 17:01:23 -0400 Subject: media: rockchip/rga: Fix bad dma_free_attrs() parameter In rga_remove(), dma_free_attrs is being passed the wrong cpu address, which triggers an exception if the driver is removed. Fix it. Tested on a RK3399 platform, with a bind/unbind cycle. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 69a2797d7bbe..ab5a6f95044a 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -925,7 +925,7 @@ static int rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, &rga->cmdbuf_virt, + dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); free_pages((unsigned long)rga->src_mmu_pages, 3); -- cgit v1.2.3-70-g09d2 From 6457b6263f0f11a9bff4e03a0141820ae9c81460 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 26 Jul 2018 06:27:15 -0400 Subject: media: adv7180: fix field type to V4L2_FIELD_ALTERNATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV7180 and ADV7182 transmit whole fields, bottom field followed by top (or vice-versa, depending on detected video standard). So for chips that do not have support for explicitly setting the field mode via I2P, set the field mode to V4L2_FIELD_ALTERNATE. I2P converts fields into frames using an edge adaptive algorithm. The frame rate is the same as the 'field rate': e.g. X fields per second are now X frames per second. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index a727d7f806a1..5a2751a688b8 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -644,6 +644,9 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd, fmt->width = 720; fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; + if (state->field == V4L2_FIELD_ALTERNATE) + fmt->height /= 2; + return 0; } @@ -711,11 +714,11 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, switch (format->format.field) { case V4L2_FIELD_NONE: - if (!(state->chip_info->flags & ADV7180_FLAG_I2P)) - format->format.field = V4L2_FIELD_INTERLACED; - break; + if (state->chip_info->flags & ADV7180_FLAG_I2P) + break; + /* fall through */ default: - format->format.field = V4L2_FIELD_INTERLACED; + format->format.field = V4L2_FIELD_ALTERNATE; break; } @@ -1291,7 +1294,7 @@ static int adv7180_probe(struct i2c_client *client, return -ENOMEM; state->client = client; - state->field = V4L2_FIELD_INTERLACED; + state->field = V4L2_FIELD_ALTERNATE; state->chip_info = (struct adv7180_chip_info *)id->driver_data; state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", -- cgit v1.2.3-70-g09d2 From 38566d28ea9114f9019e7f9804d6db5cd98bf6dd Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 26 Jul 2018 06:27:16 -0400 Subject: media: adv7180: add g_frame_interval support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement g_frame_interval to return the current standard's frame interval. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 5a2751a688b8..de10367d550b 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -461,6 +461,22 @@ static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } +static int adv7180_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct adv7180_state *state = to_state(sd); + + if (state->curr_norm & V4L2_STD_525_60) { + fi->interval.numerator = 1001; + fi->interval.denominator = 30000; + } else { + fi->interval.numerator = 1; + fi->interval.denominator = 25; + } + + return 0; +} + static void adv7180_set_power_pin(struct adv7180_state *state, bool on) { if (!state->pwdn_gpio) @@ -820,6 +836,7 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, + .g_frame_interval = adv7180_g_frame_interval, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, -- cgit v1.2.3-70-g09d2 From 17f330ce9e434333a98b0fbc7b6d5ddbe0cc6770 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Thu, 26 Jul 2018 21:47:24 -0400 Subject: media: video-i2c: hwmon: fix return value from amg88xx_hwmon_init() PTR_ERR was making any pointer passed an error pointer, and should be replaced with PTR_ERR_OR_ZERO which checks if is an actual error condition. Signed-off-by: Matt Ranostay Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/video-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 7dc9338502e5..06d29d8f6be8 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -167,7 +167,7 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data) void *hwmon = devm_hwmon_device_register_with_info(&data->client->dev, "amg88xx", data, &amg88xx_chip_info, NULL); - return PTR_ERR(hwmon); + return PTR_ERR_OR_ZERO(hwmon); } #else #define amg88xx_hwmon_init NULL -- cgit v1.2.3-70-g09d2 From 2b5c5798750180a34cb1f39cf648d8a8bceec287 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 22:58:43 -0400 Subject: media: i2c: adv7842: Replace mdelay() with msleep() and usleep_range() in adv7842_ddr_ram_test() adv7842_ddr_ram_test() is never called in atomic context. It only calls from: adv7842_ddr_ram_test() <- adv7842_command_ram_test() <- adv7842_ioctl() adv7842_ddr_ram_test() calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep() and usleep_range(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7842.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 99d781343fb1..4f8fbdd00e35 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3102,7 +3102,7 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) sdp_write(sd, 0x12, 0x00); /* Disable 3D comb, Frame TBC & 3DNR */ io_write(sd, 0xFF, 0x04); /* Reset memory controller */ - mdelay(5); + usleep_range(5000, 6000); sdp_write(sd, 0x12, 0x00); /* Disable 3D Comb, Frame TBC & 3DNR */ sdp_io_write(sd, 0x2A, 0x01); /* Memory BIST Initialisation */ @@ -3116,12 +3116,12 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) sdp_io_write(sd, 0x7d, 0x00); /* Memory BIST Initialisation */ sdp_io_write(sd, 0x7e, 0x1a); /* Memory BIST Initialisation */ - mdelay(5); + usleep_range(5000, 6000); sdp_io_write(sd, 0xd9, 0xd5); /* Enable BIST Test */ sdp_write(sd, 0x12, 0x05); /* Enable FRAME TBC & 3D COMB */ - mdelay(20); + msleep(20); for (i = 0; i < 10; i++) { u8 result = sdp_io_read(sd, 0xdb); @@ -3132,7 +3132,7 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) else pass++; } - mdelay(20); + msleep(20); } v4l2_dbg(1, debug, sd, -- cgit v1.2.3-70-g09d2 From a8a4d304b1a41105568c1408e4276cd6f32be787 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:02:40 -0400 Subject: media: i2c: vs6624: Replace mdelay() with msleep() and usleep_range() in vs6624_probe() vs6624_probe() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep() and usleep_range(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/vs6624.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 1658816a9844..bc9825f4a73d 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -770,7 +770,7 @@ static int vs6624_probe(struct i2c_client *client, return ret; } /* wait 100ms before any further i2c writes are performed */ - mdelay(100); + msleep(100); sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (sensor == NULL) @@ -782,7 +782,7 @@ static int vs6624_probe(struct i2c_client *client, vs6624_writeregs(sd, vs6624_p1); vs6624_write(sd, VS6624_MICRO_EN, 0x2); vs6624_write(sd, VS6624_DIO_EN, 0x1); - mdelay(10); + usleep_range(10000, 11000); vs6624_writeregs(sd, vs6624_p2); vs6624_writeregs(sd, vs6624_default); -- cgit v1.2.3-70-g09d2 From da27ef68751f804dbe6c2b84a49848f90ae48fd0 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:07:04 -0400 Subject: media: pci: cobalt: Replace GFP_ATOMIC with GFP_KERNEL in cobalt_probe() cobalt_probe() is never called in atomic context. It calls kzalloc() with GFP_ATOMIC, which is not necessary. GFP_ATOMIC can be replaced with GFP_KERNEL. This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/cobalt-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index c8b1a6206c65..4885e833c052 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -670,7 +670,7 @@ static int cobalt_probe(struct pci_dev *pci_dev, /* FIXME - module parameter arrays constrain max instances */ i = atomic_inc_return(&cobalt_instance) - 1; - cobalt = kzalloc(sizeof(struct cobalt), GFP_ATOMIC); + cobalt = kzalloc(sizeof(struct cobalt), GFP_KERNEL); if (cobalt == NULL) return -ENOMEM; cobalt->pci_dev = pci_dev; -- cgit v1.2.3-70-g09d2 From ccb2ca78a7278ec0024f281fdc2155b32f995cef Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:09:46 -0400 Subject: media: pci: cx23885: Replace mdelay() with msleep() and usleep_range() in altera_ci_slot_reset() altera_ci_slot_reset() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/altera-ci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c index 70aec9bb7e95..62bc8049b320 100644 --- a/drivers/media/pci/cx23885/altera-ci.c +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -346,7 +346,7 @@ static int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) mutex_unlock(&inter->fpga_mutex); for (;;) { - mdelay(50); + msleep(50); mutex_lock(&inter->fpga_mutex); -- cgit v1.2.3-70-g09d2 From 9095b23fc2a1f64b774368a12cc91b941e200512 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:15:34 -0400 Subject: media: pci: cx23885: Replace mdelay() with msleep() and usleep_range() in cx23885_gpio_setup() cx23885_gpio_setup() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep() and usleep_range(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-cards.c | 82 +++++++++++++++---------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 9f50748fdf56..ed3210dc50bc 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -1497,20 +1497,20 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the demod into reset and protect the eeprom */ mc417_gpio_clear(dev, GPIO_15 | GPIO_14); - mdelay(100); + msleep(100); /* Bring the demod and blaster out of reset */ mc417_gpio_set(dev, GPIO_15 | GPIO_14); - mdelay(100); + msleep(100); /* Force the TDA8295A into reset and back */ cx23885_gpio_enable(dev, GPIO_2, 1); cx23885_gpio_set(dev, GPIO_2); - mdelay(20); + msleep(20); cx23885_gpio_clear(dev, GPIO_2); - mdelay(20); + msleep(20); cx23885_gpio_set(dev, GPIO_2); - mdelay(20); + msleep(20); break; case CX23885_BOARD_HAUPPAUGE_HVR1200: /* GPIO-0 tda10048 demodulator reset */ @@ -1518,9 +1518,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_HAUPPAUGE_HVR1700: @@ -1539,9 +1539,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_HAUPPAUGE_HVR1400: @@ -1551,9 +1551,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: @@ -1564,9 +1564,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x000f0000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x0000000f); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -1578,9 +1578,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x000f0000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x0000000f); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: @@ -1596,9 +1596,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00040000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000004); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: @@ -1608,11 +1608,11 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); cx_set(MC417_RWD, 0x00000002); - mdelay(200); + msleep(200); cx_clear(MC417_RWD, 0x00000800); - mdelay(200); + msleep(200); cx_set(MC417_RWD, 0x00000800); - mdelay(200); + msleep(200); break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: /* GPIO-0 INTA from CiMax1 @@ -1630,7 +1630,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040000); /* GPIO as out */ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */ cx_clear(GP0_IO, 0x00030004); - mdelay(100);/* reset delay */ + msleep(100);/* reset delay */ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ /* GPIO-15 IN as ~ACK, rest as OUT */ @@ -1653,7 +1653,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1); cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5); cx23885_gpio_clear(dev, GPIO_9); - mdelay(20); + msleep(20); cx23885_gpio_set(dev, GPIO_9); break; case CX23885_BOARD_MYGICA_X8506: @@ -1664,18 +1664,18 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* GPIO-2 demod reset */ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); - mdelay(100); + msleep(100); break; case CX23885_BOARD_MYGICA_X8558PRO: /* GPIO-0 reset first ATBM8830 */ /* GPIO-1 reset second ATBM8830 */ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_0 | GPIO_1); - mdelay(100); + msleep(100); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: @@ -1699,11 +1699,11 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the demod into reset and protect the eeprom */ mc417_gpio_clear(dev, GPIO_14 | GPIO_13); - mdelay(100); + msleep(100); /* Bring the demod out of reset */ mc417_gpio_set(dev, GPIO_14); - mdelay(100); + msleep(100); /* CX24228 GPIO */ /* Connected to IF / Mux */ @@ -1728,7 +1728,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */ /* GPIO-0 as INT, reset & TMS low */ cx_clear(GP0_IO, 0x00010006); - mdelay(100);/* reset delay */ + msleep(100);/* reset delay */ cx_set(GP0_IO, 0x00000004); /* reset high */ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */ @@ -1747,36 +1747,36 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_8 | GPIO_9); - mdelay(100); + msleep(100); break; case CX23885_BOARD_AVERMEDIA_HC81R: cx_clear(MC417_CTL, 1); /* GPIO-0,1,2 setup direction as output */ cx_set(GP0_IO, 0x00070000); - mdelay(10); + usleep_range(10000, 11000); /* AF9013 demod reset */ cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_clear(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); /* demod tune? */ cx_clear(GP0_IO, 0x00030003); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00020002); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_clear(GP0_IO, 0x00020002); /* XC3028L tuner reset */ cx_set(GP0_IO, 0x00040004); cx_clear(GP0_IO, 0x00040004); cx_set(GP0_IO, 0x00040004); - mdelay(60); + msleep(60); break; case CX23885_BOARD_DVBSKY_T9580: case CX23885_BOARD_DVBSKY_S952: @@ -1785,7 +1785,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(MC417_CTL, 0x00000037); cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1); cx23885_gpio_clear(dev, GPIO_2 | GPIO_11); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_2 | GPIO_11); break; case CX23885_BOARD_DVBSKY_T980C: @@ -1807,7 +1807,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */ cx_clear(GP0_IO, 0x00010004); /* GPIO 0 as input */ - mdelay(100); /* reset delay */ + msleep(100); /* reset delay */ cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */ cx_clear(GP0_IO, 0x00010002); cx_write(MC417_CTL, 0x00000037); /* enable GPIO3-18 pins */ -- cgit v1.2.3-70-g09d2 From 71be8dee6bf13a00bab0f568ce403b3c28c5295f Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:18:46 -0400 Subject: media: pci: cx23885: Replace mdelay() with msleep() in cx23885_reset() cx23885_reset() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 94b996ff12a9..39804d830305 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -667,7 +667,7 @@ static void cx23885_reset(struct cx23885_dev *dev) /* clear dma in progress */ cx23885_clear_bridge_error(dev); - mdelay(100); + msleep(100); cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], 720*4, 0); -- cgit v1.2.3-70-g09d2 From 65155a9bbc18e4312a989a13c137c7410e1568dd Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:22:09 -0400 Subject: media: pci: cx25821: Replace mdelay() with msleep() cx25821_gpio_init(), cx25821_initialize() and cx25821_registers_init() are never called in atomic context. They call mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx25821/cx25821-core.c | 4 ++-- drivers/media/pci/cx25821/cx25821-gpio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 040c6c251d3a..2f0171134f7e 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -428,7 +428,7 @@ static void cx25821_registers_init(struct cx25821_dev *dev) tmp |= FLD_USE_ALT_PLL_REF; cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); - mdelay(100); + msleep(100); } int cx25821_sram_channel_setup(struct cx25821_dev *dev, @@ -803,7 +803,7 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); cx_write(PAD_CTRL, 0x12); /* for I2C */ cx25821_registers_init(dev); /* init Pecos registers */ - mdelay(100); + msleep(100); for (i = 0; i < VID_CHANNEL_NUM; i++) { cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); diff --git a/drivers/media/pci/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c index 76b8f619e55a..f5ffaf880e5f 100644 --- a/drivers/media/pci/cx25821/cx25821-gpio.c +++ b/drivers/media/pci/cx25821/cx25821-gpio.c @@ -88,7 +88,7 @@ void cx25821_gpio_init(struct cx25821_dev *dev) default: /* set GPIO 5 to select the path for Medusa/Athena */ cx25821_set_gpiopin_logicvalue(dev, 5, 1); - mdelay(20); + msleep(20); break; } -- cgit v1.2.3-70-g09d2 From dddaf754cb3af9fe779be1f6e117dc76e36cfdc2 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:38:23 -0400 Subject: media: pci: cx88: Replace mdelay() with msleep() in cx88_card_setup_pre_i2c() cx88_card_setup_pre_i2c() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-cards.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 4c92d2388c26..07e1483e987d 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3307,9 +3307,9 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: cx_write(MO_GP2_IO, 0xcf7); - mdelay(50); + msleep(50); cx_write(MO_GP2_IO, 0xef5); - mdelay(50); + msleep(50); cx_write(MO_GP2_IO, 0xcf7); usleep_range(10000, 20000); break; -- cgit v1.2.3-70-g09d2 From 9d08ba6de0c52d87b1f9619df3f6faacde2134b7 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:41:50 -0400 Subject: media: pci: cx88: Replace mdelay() with msleep() in dvb_register() dvb_register() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-dvb.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index ccfde28d4af2..90b208747e0f 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1226,9 +1226,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); /* Select RF connector callback */ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; @@ -1248,9 +1248,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 9); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, 0x0e, @@ -1267,9 +1267,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_5_gold, 0x0e, @@ -1289,9 +1289,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &pchdtv_hd5500, 0x59, @@ -1583,9 +1583,9 @@ static int dvb_register(struct cx8802_dev *dev) cx_set(MO_GP0_IO, 0x0101); cx_clear(MO_GP0_IO, 0x01); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 0x01); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(stv0299_attach, &samsung_stv0299_config, -- cgit v1.2.3-70-g09d2 From 5bfffa0c86915e5afbfe56d9790457b2a9887f2d Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 23:44:24 -0400 Subject: media: pci: ivtv: Replace GFP_ATOMIC with GFP_KERNEL ivtv_probe() and ivtvfb_init_card() are never called in atomic context. They call kzalloc() with GFP_ATOMIC, which is not necessary. GFP_ATOMIC can be replaced with GFP_KERNEL. This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-driver.c | 2 +- drivers/media/pci/ivtv/ivtvfb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 6b2ffdc96961..dd727098daf4 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -999,7 +999,7 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) int vbi_buf_size; struct ivtv *itv; - itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); + itv = kzalloc(sizeof(struct ivtv), GFP_KERNEL); if (itv == NULL) return -ENOMEM; itv->pdev = pdev; diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index b19058e36853..5ddaa8ed11a5 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -1178,7 +1178,7 @@ static int ivtvfb_init_card(struct ivtv *itv) } itv->osd_info = kzalloc(sizeof(struct osd_info), - GFP_ATOMIC|__GFP_NOWARN); + GFP_KERNEL|__GFP_NOWARN); if (itv->osd_info == NULL) { IVTVFB_ERR("Failed to allocate memory for osd_info\n"); return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 4070fc9ade52f7d0ad1397fe74f564ae95e68a4f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 26 Jul 2018 18:36:57 -0400 Subject: media: rcar-csi2: update stream start for V3M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Latest errata document updates the start procedure for V3M. This change in addition to adhering to the datasheet update fixes capture on early revisions of V3M. 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-csi2.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index daef72d410a3..dc5ae8025832 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -339,6 +339,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); + int (*confirm_start)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; bool clear_ulps; @@ -545,6 +546,13 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret) return ret; + /* Confirm start */ + if (priv->info->confirm_start) { + ret = priv->info->confirm_start(priv); + if (ret) + return ret; + } + /* Clear Ultra Low Power interrupt. */ if (priv->info->clear_ulps) rcsi2_write(priv, INTSTATE_REG, @@ -880,6 +888,11 @@ static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) } static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) +{ + return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); +} + +static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) { static const struct phtw_value step1[] = { { .data = 0xed, .code = 0x34 }, @@ -890,12 +903,6 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) { /* sentinel */ }, }; - int ret; - - ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); - if (ret) - return ret; - return rcsi2_phtw_write_array(priv, step1); } @@ -949,6 +956,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, + .confirm_start = rcsi2_confirm_start_v3m_e3, }; static const struct of_device_id rcar_csi2_of_table[] = { -- cgit v1.2.3-70-g09d2 From e6c17ada3188e0deb43652b0eed249f37e0d44b0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 20 Jul 2018 17:07:15 -0400 Subject: media: dw9807-vcm: Recognise this is just the VCM bit of the device The dw9807 contains a voice coil lens driver as well as an EEPROM. This driver is just for the VCM. Reflect this in the driver's name --- this is already the case for the compatible string, for instance. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/Makefile | 2 +- drivers/media/i2c/dw9807-vcm.c | 329 +++++++++++++++++++++++++++++++++++++++++ drivers/media/i2c/dw9807.c | 329 ----------------------------------------- 4 files changed, 331 insertions(+), 331 deletions(-) create mode 100644 drivers/media/i2c/dw9807-vcm.c delete mode 100644 drivers/media/i2c/dw9807.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 439f6be08b95..3a6434cdc2c0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -346,7 +346,7 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. -config VIDEO_DW9807 +config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER depends on VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 837c428339df..a6314160a3f5 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -25,7 +25,7 @@ obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o -obj-$(CONFIG_VIDEO_DW9807) += dw9807.o +obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c new file mode 100644 index 000000000000..8ba3920b6e2f --- /dev/null +++ b/drivers/media/i2c/dw9807-vcm.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DW9807_MAX_FOCUS_POS 1023 +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position. + */ +#define DW9807_FOCUS_STEPS 1 +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9807_CTRL_STEPS 16 +#define DW9807_CTRL_DELAY_US 1000 + +#define DW9807_CTL_ADDR 0x02 +/* + * DW9807 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + */ +#define DW9807_MSB_ADDR 0x03 +#define DW9807_LSB_ADDR 0x04 +#define DW9807_STATUS_ADDR 0x05 +#define DW9807_MODE_ADDR 0x06 +#define DW9807_RESONANCE_ADDR 0x07 + +#define MAX_RETRY 10 + +struct dw9807_device { + struct v4l2_ctrl_handler ctrls_vcm; + struct v4l2_subdev sd; + u16 current_val; +}; + +static inline struct dw9807_device *sd_to_dw9807_vcm( + struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9807_device, sd); +} + +static int dw9807_i2c_check(struct i2c_client *client) +{ + const char status_addr = DW9807_STATUS_ADDR; + char status_result; + int ret; + + ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); + if (ret < 0) { + dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", + ret); + return ret; + } + + ret = i2c_master_recv(client, &status_result, sizeof(status_result)); + if (ret < 0) { + dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", + ret); + return ret; + } + + return status_result; +} + +static int dw9807_set_dac(struct i2c_client *client, u16 data) +{ + const char tx_data[3] = { + DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) + }; + int val, ret; + + /* + * According to the datasheet, need to check the bus status before we + * write VCM position. This ensure that we really write the value + * into the register + */ + ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, + DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); + + if (ret || val < 0) { + if (ret) { + dev_warn(&client->dev, + "Cannot do the write operation because VCM is busy\n"); + } + + return ret ? -EBUSY : val; + } + + /* Write VCM position to registers */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, + "I2C write MSB fail ret=%d\n", ret); + + return ret; + } + + return 0; +} + +static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9807_device *dev_vcm = container_of(ctrl->handler, + struct dw9807_device, ctrls_vcm); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + dev_vcm->current_val = ctrl->val; + return dw9807_set_dac(client, ctrl->val); + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { + .s_ctrl = dw9807_set_ctrl, +}; + +static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rval; + + rval = pm_runtime_get_sync(sd->dev); + if (rval < 0) { + pm_runtime_put_noidle(sd->dev); + return rval; + } + + return 0; +} + +static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9807_int_ops = { + .open = dw9807_open, + .close = dw9807_close, +}; + +static const struct v4l2_subdev_ops dw9807_ops = { }; + +static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) +{ + v4l2_async_unregister_subdev(&dw9807_dev->sd); + v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); + media_entity_cleanup(&dw9807_dev->sd.entity); +} + +static int dw9807_init_controls(struct dw9807_device *dev_vcm) +{ + struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; + const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + v4l2_ctrl_handler_init(hdl, 1); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, + 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); + + dev_vcm->sd.ctrl_handler = hdl; + if (hdl->error) { + dev_err(&client->dev, "%s fail error: 0x%x\n", + __func__, hdl->error); + return hdl->error; + } + + return 0; +} + +static int dw9807_probe(struct i2c_client *client) +{ + struct dw9807_device *dw9807_dev; + int rval; + + dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), + GFP_KERNEL); + if (dw9807_dev == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); + dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9807_dev->sd.internal_ops = &dw9807_int_ops; + + rval = dw9807_init_controls(dw9807_dev); + if (rval) + goto err_cleanup; + + rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); + if (rval < 0) + goto err_cleanup; + + dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; + + rval = v4l2_async_register_subdev(&dw9807_dev->sd); + if (rval < 0) + goto err_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +err_cleanup: + dw9807_subdev_cleanup(dw9807_dev); + + return rval; +} + +static int dw9807_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + dw9807_subdev_cleanup(dw9807_dev); + + return 0; +} + +/* + * This function sets the vcm position, so it consumes least current + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; + int ret, val; + + for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); + val >= 0; val -= DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_once(dev, "%s I2C failure: %d", __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + /* Power down */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + return 0; +} + +/* + * This function sets the vcm position to the value set by the user + * through v4l2_ctrl_ops s_ctrl handler + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; + int ret, val; + + /* Power on */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; + val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; + val += DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_ratelimited(dev, "%s I2C failure: %d", + __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + return 0; +} + +static const struct of_device_id dw9807_of_table[] = { + { .compatible = "dongwoon,dw9807-vcm" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw9807_of_table); + +static const struct dev_pm_ops dw9807_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) + SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) +}; + +static struct i2c_driver dw9807_i2c_driver = { + .driver = { + .name = "dw9807", + .pm = &dw9807_pm_ops, + .of_match_table = dw9807_of_table, + }, + .probe_new = dw9807_probe, + .remove = dw9807_remove, +}; + +module_i2c_driver(dw9807_i2c_driver); + +MODULE_AUTHOR("Chiang, Alan "); +MODULE_DESCRIPTION("DW9807 VCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/dw9807.c b/drivers/media/i2c/dw9807.c deleted file mode 100644 index 8ba3920b6e2f..000000000000 --- a/drivers/media/i2c/dw9807.c +++ /dev/null @@ -1,329 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2018 Intel Corporation - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DW9807_MAX_FOCUS_POS 1023 -/* - * This sets the minimum granularity for the focus positions. - * A value of 1 gives maximum accuracy for a desired focus position. - */ -#define DW9807_FOCUS_STEPS 1 -/* - * This acts as the minimum granularity of lens movement. - * Keep this value power of 2, so the control steps can be - * uniformly adjusted for gradual lens movement, with desired - * number of control steps. - */ -#define DW9807_CTRL_STEPS 16 -#define DW9807_CTRL_DELAY_US 1000 - -#define DW9807_CTL_ADDR 0x02 -/* - * DW9807 separates two registers to control the VCM position. - * One for MSB value, another is LSB value. - */ -#define DW9807_MSB_ADDR 0x03 -#define DW9807_LSB_ADDR 0x04 -#define DW9807_STATUS_ADDR 0x05 -#define DW9807_MODE_ADDR 0x06 -#define DW9807_RESONANCE_ADDR 0x07 - -#define MAX_RETRY 10 - -struct dw9807_device { - struct v4l2_ctrl_handler ctrls_vcm; - struct v4l2_subdev sd; - u16 current_val; -}; - -static inline struct dw9807_device *sd_to_dw9807_vcm( - struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct dw9807_device, sd); -} - -static int dw9807_i2c_check(struct i2c_client *client) -{ - const char status_addr = DW9807_STATUS_ADDR; - char status_result; - int ret; - - ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); - if (ret < 0) { - dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", - ret); - return ret; - } - - ret = i2c_master_recv(client, &status_result, sizeof(status_result)); - if (ret < 0) { - dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", - ret); - return ret; - } - - return status_result; -} - -static int dw9807_set_dac(struct i2c_client *client, u16 data) -{ - const char tx_data[3] = { - DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) - }; - int val, ret; - - /* - * According to the datasheet, need to check the bus status before we - * write VCM position. This ensure that we really write the value - * into the register - */ - ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, - DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); - - if (ret || val < 0) { - if (ret) { - dev_warn(&client->dev, - "Cannot do the write operation because VCM is busy\n"); - } - - return ret ? -EBUSY : val; - } - - /* Write VCM position to registers */ - ret = i2c_master_send(client, tx_data, sizeof(tx_data)); - if (ret < 0) { - dev_err(&client->dev, - "I2C write MSB fail ret=%d\n", ret); - - return ret; - } - - return 0; -} - -static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct dw9807_device *dev_vcm = container_of(ctrl->handler, - struct dw9807_device, ctrls_vcm); - - if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { - struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); - - dev_vcm->current_val = ctrl->val; - return dw9807_set_dac(client, ctrl->val); - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { - .s_ctrl = dw9807_set_ctrl, -}; - -static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - int rval; - - rval = pm_runtime_get_sync(sd->dev); - if (rval < 0) { - pm_runtime_put_noidle(sd->dev); - return rval; - } - - return 0; -} - -static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - pm_runtime_put(sd->dev); - - return 0; -} - -static const struct v4l2_subdev_internal_ops dw9807_int_ops = { - .open = dw9807_open, - .close = dw9807_close, -}; - -static const struct v4l2_subdev_ops dw9807_ops = { }; - -static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) -{ - v4l2_async_unregister_subdev(&dw9807_dev->sd); - v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); - media_entity_cleanup(&dw9807_dev->sd.entity); -} - -static int dw9807_init_controls(struct dw9807_device *dev_vcm) -{ - struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; - const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; - struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); - - v4l2_ctrl_handler_init(hdl, 1); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, - 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); - - dev_vcm->sd.ctrl_handler = hdl; - if (hdl->error) { - dev_err(&client->dev, "%s fail error: 0x%x\n", - __func__, hdl->error); - return hdl->error; - } - - return 0; -} - -static int dw9807_probe(struct i2c_client *client) -{ - struct dw9807_device *dw9807_dev; - int rval; - - dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), - GFP_KERNEL); - if (dw9807_dev == NULL) - return -ENOMEM; - - v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); - dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - dw9807_dev->sd.internal_ops = &dw9807_int_ops; - - rval = dw9807_init_controls(dw9807_dev); - if (rval) - goto err_cleanup; - - rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); - if (rval < 0) - goto err_cleanup; - - dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; - - rval = v4l2_async_register_subdev(&dw9807_dev->sd); - if (rval < 0) - goto err_cleanup; - - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - - return 0; - -err_cleanup: - dw9807_subdev_cleanup(dw9807_dev); - - return rval; -} - -static int dw9807_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - dw9807_subdev_cleanup(dw9807_dev); - - return 0; -} - -/* - * This function sets the vcm position, so it consumes least current - * The lens position is gradually moved in units of DW9807_CTRL_STEPS, - * to make the movements smoothly. - */ -static int __maybe_unused dw9807_vcm_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); - const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; - int ret, val; - - for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); - val >= 0; val -= DW9807_CTRL_STEPS) { - ret = dw9807_set_dac(client, val); - if (ret) - dev_err_once(dev, "%s I2C failure: %d", __func__, ret); - usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); - } - - /* Power down */ - ret = i2c_master_send(client, tx_data, sizeof(tx_data)); - if (ret < 0) { - dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); - return ret; - } - - return 0; -} - -/* - * This function sets the vcm position to the value set by the user - * through v4l2_ctrl_ops s_ctrl handler - * The lens position is gradually moved in units of DW9807_CTRL_STEPS, - * to make the movements smoothly. - */ -static int __maybe_unused dw9807_vcm_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); - const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; - int ret, val; - - /* Power on */ - ret = i2c_master_send(client, tx_data, sizeof(tx_data)); - if (ret < 0) { - dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); - return ret; - } - - for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; - val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; - val += DW9807_CTRL_STEPS) { - ret = dw9807_set_dac(client, val); - if (ret) - dev_err_ratelimited(dev, "%s I2C failure: %d", - __func__, ret); - usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); - } - - return 0; -} - -static const struct of_device_id dw9807_of_table[] = { - { .compatible = "dongwoon,dw9807-vcm" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, dw9807_of_table); - -static const struct dev_pm_ops dw9807_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) - SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) -}; - -static struct i2c_driver dw9807_i2c_driver = { - .driver = { - .name = "dw9807", - .pm = &dw9807_pm_ops, - .of_match_table = dw9807_of_table, - }, - .probe_new = dw9807_probe, - .remove = dw9807_remove, -}; - -module_i2c_driver(dw9807_i2c_driver); - -MODULE_AUTHOR("Chiang, Alan "); -MODULE_DESCRIPTION("DW9807 VCM driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From aab7ed1c392703604fbdc5bd5005dfb61a0b32f9 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 25 Jul 2018 09:44:31 -0400 Subject: media: i2c: Add driver for Aptina MT9V111 Add V4L2 sensor driver for Aptina MT9V111 CMOS image sensor. The MT9V111 is a 1/4-Inch CMOS image sensor based on MT9V011 with an integrated Image Flow Processor. Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/mt9v111.c | 1298 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1310 insertions(+) create mode 100644 drivers/media/i2c/mt9v111.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 3a6434cdc2c0..f4892f2b9788 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -861,6 +861,17 @@ config VIDEO_MT9V032 This is a Video4Linux2 sensor driver for the Micron MT9V032 752x480 CMOS sensor. +config VIDEO_MT9V111 + tristate "Aptina MT9V111 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Aptina/Micron + MT9V111 sensor. + + To compile this driver as a module, choose M here: the + module will be called mt9v111. + config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a6314160a3f5..804d0818da18 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c new file mode 100644 index 000000000000..da8f6ab91307 --- /dev/null +++ b/drivers/media/i2c/mt9v111.c @@ -0,0 +1,1298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 sensor driver for Aptina MT9V111 image sensor + * Copyright (C) 2018 Jacopo Mondi + * + * Based on mt9v032 driver + * Copyright (C) 2010, Laurent Pinchart + * Copyright (C) 2008, Guennadi Liakhovetski + * + * Based on mt9v011 driver + * Copyright (c) 2009 Mauro Carvalho Chehab + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * MT9V111 is a 1/4-Inch CMOS digital image sensor with an integrated + * Image Flow Processing (IFP) engine and a sensor core loosely based on + * MT9V011. + * + * The IFP can produce several output image formats from the sensor core + * output. This driver currently supports only YUYV format permutations. + * + * The driver allows manual frame rate control through s_frame_interval subdev + * operation or V4L2_CID_V/HBLANK controls, but it is known that the + * auto-exposure algorithm might modify the programmed frame rate. While the + * driver initially programs the sensor with auto-exposure and + * auto-white-balancing enabled, it is possible to disable them and more + * precisely control the frame rate. + * + * While it seems possible to instruct the auto-exposure control algorithm to + * respect a programmed frame rate when adjusting the pixel integration time, + * registers controlling this feature are not documented in the public + * available sensor manual used to develop this driver (09005aef80e90084, + * MT9V111_1.fm - Rev. G 1/05 EN). + */ + +#define MT9V111_CHIP_ID_HIGH 0x82 +#define MT9V111_CHIP_ID_LOW 0x3a + +#define MT9V111_R01_ADDR_SPACE 0x01 +#define MT9V111_R01_IFP 0x01 +#define MT9V111_R01_CORE 0x04 + +#define MT9V111_IFP_R06_OPMODE_CTRL 0x06 +#define MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN BIT(1) +#define MT9V111_IFP_R06_OPMODE_CTRL_AE_EN BIT(14) +#define MT9V111_IFP_R07_IFP_RESET 0x07 +#define MT9V111_IFP_R07_IFP_RESET_MASK BIT(0) +#define MT9V111_IFP_R08_OUTFMT_CTRL 0x08 +#define MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER BIT(11) +#define MT9V111_IFP_R08_OUTFMT_CTRL_PCLK BIT(5) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2 0x3a +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR BIT(0) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC BIT(1) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK GENMASK(2, 0) +#define MT9V111_IFP_RA5_HPAN 0xa5 +#define MT9V111_IFP_RA6_HZOOM 0xa6 +#define MT9V111_IFP_RA7_HOUT 0xa7 +#define MT9V111_IFP_RA8_VPAN 0xa8 +#define MT9V111_IFP_RA9_VZOOM 0xa9 +#define MT9V111_IFP_RAA_VOUT 0xaa +#define MT9V111_IFP_DECIMATION_MASK GENMASK(9, 0) +#define MT9V111_IFP_DECIMATION_FREEZE BIT(15) + +#define MT9V111_CORE_R03_WIN_HEIGHT 0x03 +#define MT9V111_CORE_R03_WIN_V_OFFS 2 +#define MT9V111_CORE_R04_WIN_WIDTH 0x04 +#define MT9V111_CORE_R04_WIN_H_OFFS 114 +#define MT9V111_CORE_R05_HBLANK 0x05 +#define MT9V111_CORE_R05_MIN_HBLANK 0x09 +#define MT9V111_CORE_R05_MAX_HBLANK GENMASK(9, 0) +#define MT9V111_CORE_R05_DEF_HBLANK 0x26 +#define MT9V111_CORE_R06_VBLANK 0x06 +#define MT9V111_CORE_R06_MIN_VBLANK 0x03 +#define MT9V111_CORE_R06_MAX_VBLANK GENMASK(11, 0) +#define MT9V111_CORE_R06_DEF_VBLANK 0x04 +#define MT9V111_CORE_R07_OUT_CTRL 0x07 +#define MT9V111_CORE_R07_OUT_CTRL_SAMPLE BIT(4) +#define MT9V111_CORE_R09_PIXEL_INT 0x09 +#define MT9V111_CORE_R09_PIXEL_INT_MASK GENMASK(11, 0) +#define MT9V111_CORE_R0D_CORE_RESET 0x0d +#define MT9V111_CORE_R0D_CORE_RESET_MASK BIT(0) +#define MT9V111_CORE_RFF_CHIP_VER 0xff + +#define MT9V111_PIXEL_ARRAY_WIDTH 640 +#define MT9V111_PIXEL_ARRAY_HEIGHT 480 + +#define MT9V111_MAX_CLKIN 27000000 + +/* The default sensor configuration at startup time. */ +static struct v4l2_mbus_framefmt mt9v111_def_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, +}; + +struct mt9v111_dev { + struct device *dev; + struct i2c_client *client; + + u8 addr_space; + + struct v4l2_subdev sd; +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + struct media_pad pad; +#endif + + struct v4l2_ctrl *auto_awb; + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl_handler ctrls; + + /* Output image format and sizes. */ + struct v4l2_mbus_framefmt fmt; + unsigned int fps; + + /* Protects power up/down sequences. */ + struct mutex pwr_mutex; + int pwr_count; + + /* Protects stream on/off sequences. */ + struct mutex stream_mutex; + bool streaming; + + /* Flags to mark HW settings as not yet applied. */ + bool pending; + + /* Clock provider and system clock frequency. */ + struct clk *clk; + u32 sysclk; + + struct gpio_desc *oe; + struct gpio_desc *standby; + struct gpio_desc *reset; +}; + +#define sd_to_mt9v111(__sd) container_of((__sd), struct mt9v111_dev, sd) + +/* + * mt9v111_mbus_fmt - List all media bus formats supported by the driver. + * + * Only list the media bus code here. The image sizes are freely configurable + * in the pixel array sizes range. + * + * The desired frame interval, in the supported frame interval range, is + * obtained by configuring blanking as the sensor does not have a PLL but + * only a fixed clock divider that generates the output pixel clock. + */ +static struct mt9v111_mbus_fmt { + u32 code; +} mt9v111_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + }, +}; + +static u32 mt9v111_frame_intervals[] = {5, 10, 15, 20, 30}; + +/* + * mt9v111_frame_sizes - List sensor's supported resolutions. + * + * Resolution generated through decimation in the IFP block from the + * full VGA pixel array. + */ +static struct v4l2_rect mt9v111_frame_sizes[] = { + { + .width = 640, + .height = 480, + }, + { + .width = 352, + .height = 288 + }, + { + .width = 320, + .height = 240, + }, + { + .width = 176, + .height = 144, + }, + { + .width = 160, + .height = 120, + }, +}; + +/* --- Device I/O access --- */ + +static int __mt9v111_read(struct i2c_client *c, u8 reg, u16 *val) +{ + struct i2c_msg msg[2]; + __be16 buf; + int ret; + + msg[0].addr = c->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].addr = c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = (char *)&buf; + + ret = i2c_transfer(c->adapter, msg, 2); + if (ret < 0) { + dev_err(&c->dev, "i2c read transfer error: %d\n", ret); + return ret; + } + + *val = be16_to_cpu(buf); + + dev_dbg(&c->dev, "%s: %x=%x\n", __func__, reg, *val); + + return 0; +} + +static int __mt9v111_write(struct i2c_client *c, u8 reg, u16 val) +{ + struct i2c_msg msg; + u8 buf[3] = { 0 }; + int ret; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + + msg.addr = c->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = (char *)buf; + + dev_dbg(&c->dev, "%s: %x = %x%x\n", __func__, reg, buf[1], buf[2]); + + ret = i2c_transfer(c->adapter, &msg, 1); + if (ret < 0) { + dev_err(&c->dev, "i2c write transfer error: %d\n", ret); + return ret; + } + + return 0; +} + +static int __mt9v111_addr_space_select(struct i2c_client *c, u16 addr_space) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + u16 val; + int ret; + + if (mt9v111->addr_space == addr_space) + return 0; + + ret = __mt9v111_write(c, MT9V111_R01_ADDR_SPACE, addr_space); + if (ret) + return ret; + + /* Verify address space has been updated */ + ret = __mt9v111_read(c, MT9V111_R01_ADDR_SPACE, &val); + if (ret) + return ret; + + if (val != addr_space) + return -EINVAL; + + mt9v111->addr_space = addr_space; + + return 0; +} + +static int mt9v111_read(struct i2c_client *c, u8 addr_space, u8 reg, u16 *val) +{ + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + ret = __mt9v111_read(c, reg, val); + if (ret) + return ret; + + return 0; +} + +static int mt9v111_write(struct i2c_client *c, u8 addr_space, u8 reg, u16 val) +{ + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + ret = __mt9v111_write(c, reg, val); + if (ret) + return ret; + + return 0; +} + +static int mt9v111_update(struct i2c_client *c, u8 addr_space, u8 reg, + u16 mask, u16 val) +{ + u16 current_val; + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + /* Read the current register value, then update it. */ + ret = __mt9v111_read(c, reg, ¤t_val); + if (ret) + return ret; + + current_val &= ~mask; + current_val |= (val & mask); + ret = __mt9v111_write(c, reg, current_val); + if (ret) + return ret; + + return 0; +} + +/* --- Sensor HW operations --- */ + +static int __mt9v111_power_on(struct v4l2_subdev *sd) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + int ret; + + ret = clk_prepare_enable(mt9v111->clk); + if (ret) + return ret; + + clk_set_rate(mt9v111->clk, mt9v111->sysclk); + + gpiod_set_value(mt9v111->standby, 0); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->oe, 1); + usleep_range(500, 1000); + + return 0; +} + +static int __mt9v111_power_off(struct v4l2_subdev *sd) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + + gpiod_set_value(mt9v111->oe, 0); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->standby, 1); + usleep_range(500, 1000); + + clk_disable_unprepare(mt9v111->clk); + + return 0; +} + +static int __mt9v111_hw_reset(struct mt9v111_dev *mt9v111) +{ + if (!mt9v111->reset) + return -EINVAL; + + gpiod_set_value(mt9v111->reset, 1); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->reset, 0); + usleep_range(500, 1000); + + return 0; +} + +static int __mt9v111_sw_reset(struct mt9v111_dev *mt9v111) +{ + struct i2c_client *c = mt9v111->client; + int ret; + + /* Software reset core and IFP blocks. */ + + ret = mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R0D_CORE_RESET, + MT9V111_CORE_R0D_CORE_RESET_MASK, 1); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R0D_CORE_RESET, + MT9V111_CORE_R0D_CORE_RESET_MASK, 0); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_IFP, + MT9V111_IFP_R07_IFP_RESET, + MT9V111_IFP_R07_IFP_RESET_MASK, 1); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_IFP, + MT9V111_IFP_R07_IFP_RESET, + MT9V111_IFP_R07_IFP_RESET_MASK, 0); + if (ret) + return ret; + usleep_range(500, 1000); + + return 0; +} + +static int mt9v111_calc_frame_rate(struct mt9v111_dev *mt9v111, + struct v4l2_fract *tpf) +{ + unsigned int fps = tpf->numerator ? + tpf->denominator / tpf->numerator : + tpf->denominator; + unsigned int best_diff; + unsigned int frm_cols; + unsigned int row_pclk; + unsigned int best_fps; + unsigned int pclk; + unsigned int diff; + unsigned int idx; + unsigned int hb; + unsigned int vb; + unsigned int i; + int ret; + + /* Approximate to the closest supported frame interval. */ + best_diff = ~0L; + for (i = 0, idx = 0; i < ARRAY_SIZE(mt9v111_frame_intervals); i++) { + diff = abs(fps - mt9v111_frame_intervals[i]); + if (diff < best_diff) { + idx = i; + best_diff = diff; + } + } + fps = mt9v111_frame_intervals[idx]; + + /* + * The sensor does not provide a PLL circuitry and pixel clock is + * generated dividing the master clock source by two. + * + * Trow = (W + Hblank + 114) * 2 * (1 / SYSCLK) + * TFrame = Trow * (H + Vblank + 2) + * + * FPS = (SYSCLK / 2) / (Trow * (H + Vblank + 2)) + * + * This boils down to tune H and V blanks to best approximate the + * above equation. + * + * Test all available H/V blank values, until we reach the + * desired frame rate. + */ + best_fps = vb = hb = 0; + pclk = DIV_ROUND_CLOSEST(mt9v111->sysclk, 2); + row_pclk = MT9V111_PIXEL_ARRAY_WIDTH + 7 + MT9V111_CORE_R04_WIN_H_OFFS; + frm_cols = MT9V111_PIXEL_ARRAY_HEIGHT + 7 + MT9V111_CORE_R03_WIN_V_OFFS; + + best_diff = ~0L; + for (vb = MT9V111_CORE_R06_MIN_VBLANK; + vb < MT9V111_CORE_R06_MAX_VBLANK; vb++) { + for (hb = MT9V111_CORE_R05_MIN_HBLANK; + hb < MT9V111_CORE_R05_MAX_HBLANK; hb += 10) { + unsigned int t_frame = (row_pclk + hb) * + (frm_cols + vb); + unsigned int t_fps = DIV_ROUND_CLOSEST(pclk, t_frame); + + diff = abs(fps - t_fps); + if (diff < best_diff) { + best_diff = diff; + best_fps = t_fps; + + if (diff == 0) + break; + } + } + + if (diff == 0) + break; + } + + ret = v4l2_ctrl_s_ctrl_int64(mt9v111->hblank, hb); + if (ret) + return ret; + + ret = v4l2_ctrl_s_ctrl_int64(mt9v111->vblank, vb); + if (ret) + return ret; + + tpf->numerator = 1; + tpf->denominator = best_fps; + + return 0; +} + +static int mt9v111_hw_config(struct mt9v111_dev *mt9v111) +{ + struct i2c_client *c = mt9v111->client; + unsigned int ret; + u16 outfmtctrl2; + + /* Force device reset. */ + ret = __mt9v111_hw_reset(mt9v111); + if (ret == -EINVAL) + ret = __mt9v111_sw_reset(mt9v111); + if (ret) + return ret; + + /* Configure internal clock sample rate. */ + ret = mt9v111->sysclk < DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? + mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R07_OUT_CTRL, + MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 1) : + mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R07_OUT_CTRL, + MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 0); + if (ret) + return ret; + + /* + * Configure output image format components ordering. + * + * TODO: IFP block can also output several RGB permutations, we only + * support YUYV permutations at the moment. + */ + switch (mt9v111->fmt.code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC | + MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + outfmtctrl2 = 0; + break; + } + + ret = mt9v111_update(c, MT9V111_R01_IFP, MT9V111_IFP_R3A_OUTFMT_CTRL2, + MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK, + outfmtctrl2); + if (ret) + return ret; + + /* + * Do not change default sensor's core configuration: + * output the whole 640x480 pixel array, skip 18 columns and 6 rows. + * + * Instead, control the output image size through IFP block. + * + * TODO: No zoom&pan support. Currently we control the output image + * size only through decimation, with no zoom support. + */ + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA5_HPAN, + MT9V111_IFP_DECIMATION_FREEZE); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA8_VPAN, + MT9V111_IFP_DECIMATION_FREEZE); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA6_HZOOM, + MT9V111_IFP_DECIMATION_FREEZE | + MT9V111_PIXEL_ARRAY_WIDTH); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA9_VZOOM, + MT9V111_IFP_DECIMATION_FREEZE | + MT9V111_PIXEL_ARRAY_HEIGHT); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA7_HOUT, + MT9V111_IFP_DECIMATION_FREEZE | + mt9v111->fmt.width); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RAA_VOUT, + mt9v111->fmt.height); + if (ret) + return ret; + + /* Apply controls to set auto exp, auto awb and timings */ + ret = v4l2_ctrl_handler_setup(&mt9v111->ctrls); + if (ret) + return ret; + + /* + * Set pixel integration time to the whole frame time. + * This value controls the the shutter delay when running with AE + * disabled. If longer than frame time, it affects the output + * frame rate. + */ + return mt9v111_write(c, MT9V111_R01_CORE, MT9V111_CORE_R09_PIXEL_INT, + MT9V111_PIXEL_ARRAY_HEIGHT); +} + +/* --- V4L2 subdev operations --- */ + +static int mt9v111_s_power(struct v4l2_subdev *sd, int on) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + int pwr_count; + int ret = 0; + + mutex_lock(&mt9v111->pwr_mutex); + + /* + * Make sure we're transitioning from 0 to 1, or viceversa, + * before actually changing the power state. + */ + pwr_count = mt9v111->pwr_count; + pwr_count += on ? 1 : -1; + if (pwr_count == !!on) { + ret = on ? __mt9v111_power_on(sd) : + __mt9v111_power_off(sd); + if (!ret) + /* All went well, updated power counter. */ + mt9v111->pwr_count = pwr_count; + + mutex_unlock(&mt9v111->pwr_mutex); + + return ret; + } + + /* + * Update power counter to keep track of how many nested calls we + * received. + */ + WARN_ON(pwr_count < 0 || pwr_count > 1); + mt9v111->pwr_count = pwr_count; + + mutex_unlock(&mt9v111->pwr_mutex); + + return ret; +} + +static int mt9v111_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + int ret; + + mutex_lock(&mt9v111->stream_mutex); + + if (mt9v111->streaming == enable) { + mutex_unlock(&mt9v111->stream_mutex); + return 0; + } + + ret = mt9v111_s_power(subdev, enable); + if (ret) + goto error_unlock; + + if (enable && mt9v111->pending) { + ret = mt9v111_hw_config(mt9v111); + if (ret) + goto error_unlock; + + /* + * No need to update control here as far as only H/VBLANK are + * supported and immediately programmed to registers in .s_ctrl + */ + + mt9v111->pending = false; + } + + mt9v111->streaming = enable ? true : false; + mutex_unlock(&mt9v111->stream_mutex); + + return 0; + +error_unlock: + mutex_unlock(&mt9v111->stream_mutex); + + return ret; +} + +static int mt9v111_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + struct v4l2_fract *tpf = &ival->interval; + unsigned int fps = tpf->numerator ? + tpf->denominator / tpf->numerator : + tpf->denominator; + unsigned int max_fps; + + if (!tpf->numerator) + tpf->numerator = 1; + + mutex_lock(&mt9v111->stream_mutex); + + if (mt9v111->streaming) { + mutex_unlock(&mt9v111->stream_mutex); + return -EBUSY; + } + + if (mt9v111->fps == fps) { + mutex_unlock(&mt9v111->stream_mutex); + return 0; + } + + /* Make sure frame rate/image sizes constraints are respected. */ + if (mt9v111->fmt.width < QVGA_WIDTH && + mt9v111->fmt.height < QVGA_HEIGHT) + max_fps = 90; + else if (mt9v111->fmt.width < CIF_WIDTH && + mt9v111->fmt.height < CIF_HEIGHT) + max_fps = 60; + else + max_fps = mt9v111->sysclk < + DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? 15 : + 30; + + if (fps > max_fps) { + mutex_unlock(&mt9v111->stream_mutex); + return -EINVAL; + } + + mt9v111_calc_frame_rate(mt9v111, tpf); + + mt9v111->fps = fps; + mt9v111->pending = true; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + struct v4l2_fract *tpf = &ival->interval; + + mutex_lock(&mt9v111->stream_mutex); + + tpf->numerator = 1; + tpf->denominator = mt9v111->fps; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( + struct mt9v111_dev *mt9v111, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + return v4l2_subdev_get_try_format(&mt9v111->sd, cfg, pad); +#else + return &cfg->try_fmt; +#endif + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v111->fmt; + default: + return NULL; + } +} + +static int mt9v111_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > ARRAY_SIZE(mt9v111_formats) - 1) + return -EINVAL; + + code->code = mt9v111_formats[code->index].code; + + return 0; +} + +static int mt9v111_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + unsigned int i; + + if (fie->pad || fie->index >= ARRAY_SIZE(mt9v111_frame_intervals)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) + if (fie->width == mt9v111_frame_sizes[i].width && + fie->height == mt9v111_frame_sizes[i].height) + break; + + if (i == ARRAY_SIZE(mt9v111_frame_sizes)) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = mt9v111_frame_intervals[fie->index]; + + return 0; +} + +static int mt9v111_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->pad || fse->index > ARRAY_SIZE(mt9v111_frame_sizes)) + return -EINVAL; + + fse->min_width = mt9v111_frame_sizes[fse->index].width; + fse->max_width = mt9v111_frame_sizes[fse->index].width; + fse->min_height = mt9v111_frame_sizes[fse->index].height; + fse->max_height = mt9v111_frame_sizes[fse->index].height; + + return 0; +} + +static int mt9v111_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + + if (format->pad) + return -EINVAL; + + mutex_lock(&mt9v111->stream_mutex); + format->format = *__mt9v111_get_pad_format(mt9v111, cfg, format->pad, + format->which); + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + struct v4l2_mbus_framefmt new_fmt; + struct v4l2_mbus_framefmt *__fmt; + unsigned int best_fit = ~0L; + unsigned int idx; + unsigned int i; + + mutex_lock(&mt9v111->stream_mutex); + if (mt9v111->streaming) { + mutex_unlock(&mt9v111->stream_mutex); + return -EBUSY; + } + + if (format->pad) { + mutex_unlock(&mt9v111->stream_mutex); + return -EINVAL; + } + + /* Update mbus format code and sizes. */ + for (i = 0; i < ARRAY_SIZE(mt9v111_formats); i++) { + if (format->format.code == mt9v111_formats[i].code) { + new_fmt.code = mt9v111_formats[i].code; + break; + } + } + if (i == ARRAY_SIZE(mt9v111_formats)) + new_fmt.code = mt9v111_formats[0].code; + + for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) { + unsigned int fit = abs(mt9v111_frame_sizes[i].width - + format->format.width) + + abs(mt9v111_frame_sizes[i].height - + format->format.height); + if (fit < best_fit) { + best_fit = fit; + idx = i; + + if (fit == 0) + break; + } + } + new_fmt.width = mt9v111_frame_sizes[idx].width; + new_fmt.height = mt9v111_frame_sizes[idx].height; + + /* Update the device (or pad) format if it has changed. */ + __fmt = __mt9v111_get_pad_format(mt9v111, cfg, format->pad, + format->which); + + /* Format hasn't changed, stop here. */ + if (__fmt->code == new_fmt.code && + __fmt->width == new_fmt.width && + __fmt->height == new_fmt.height) + goto done; + + /* Update the format and sizes, then mark changes as pending. */ + __fmt->code = new_fmt.code; + __fmt->width = new_fmt.width; + __fmt->height = new_fmt.height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9v111->pending = true; + + dev_dbg(mt9v111->dev, "%s: mbus_code: %x - (%ux%u)\n", + __func__, __fmt->code, __fmt->width, __fmt->height); + +done: + format->format = *__fmt; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + cfg->try_fmt = mt9v111_def_fmt; + + return 0; +} + +static const struct v4l2_subdev_core_ops mt9v111_core_ops = { + .s_power = mt9v111_s_power, +}; + +static const struct v4l2_subdev_video_ops mt9v111_video_ops = { + .s_stream = mt9v111_s_stream, + .s_frame_interval = mt9v111_s_frame_interval, + .g_frame_interval = mt9v111_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { + .init_cfg = mt9v111_init_cfg, + .enum_mbus_code = mt9v111_enum_mbus_code, + .enum_frame_size = mt9v111_enum_frame_size, + .enum_frame_interval = mt9v111_enum_frame_interval, + .get_fmt = mt9v111_get_format, + .set_fmt = mt9v111_set_format, +}; + +static const struct v4l2_subdev_ops mt9v111_ops = { + .core = &mt9v111_core_ops, + .video = &mt9v111_video_ops, + .pad = &mt9v111_pad_ops, +}; + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) +static const struct media_entity_operations mt9v111_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; +#endif + +/* --- V4L2 ctrl --- */ +static int mt9v111_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v111_dev *mt9v111 = container_of(ctrl->handler, + struct mt9v111_dev, + ctrls); + int ret; + + mutex_lock(&mt9v111->pwr_mutex); + /* + * If sensor is powered down, just cache new control values, + * no actual register access. + */ + if (!mt9v111->pwr_count) { + mt9v111->pending = true; + mutex_unlock(&mt9v111->pwr_mutex); + return 0; + } + mutex_unlock(&mt9v111->pwr_mutex); + + /* + * Flickering control gets disabled if both auto exp and auto awb + * are disabled too. If any of the two is enabled, enable it. + * + * Disabling flickering when ae and awb are off allows a more precise + * control of the programmed frame rate. + */ + if (mt9v111->auto_exp->is_new || mt9v111->auto_awb->is_new) { + if (mt9v111->auto_exp->val == V4L2_EXPOSURE_MANUAL && + mt9v111->auto_awb->val == V4L2_WHITE_BALANCE_MANUAL) + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R08_OUTFMT_CTRL, + MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, + 0); + else + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R08_OUTFMT_CTRL, + MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, + 1); + if (ret) + return ret; + } + + ret = -EINVAL; + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R06_OPMODE_CTRL, + MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN, + ctrl->val == V4L2_WHITE_BALANCE_AUTO ? + MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN : 0); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R06_OPMODE_CTRL, + MT9V111_IFP_R06_OPMODE_CTRL_AE_EN, + ctrl->val == V4L2_EXPOSURE_AUTO ? + MT9V111_IFP_R06_OPMODE_CTRL_AE_EN : 0); + break; + case V4L2_CID_HBLANK: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_R05_HBLANK, + MT9V111_CORE_R05_MAX_HBLANK, + mt9v111->hblank->val); + break; + case V4L2_CID_VBLANK: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_R06_VBLANK, + MT9V111_CORE_R06_MAX_VBLANK, + mt9v111->vblank->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops mt9v111_ctrl_ops = { + .s_ctrl = mt9v111_s_ctrl, +}; + +static int mt9v111_chip_probe(struct mt9v111_dev *mt9v111) +{ + int ret; + u16 val; + + ret = __mt9v111_power_on(&mt9v111->sd); + if (ret) + return ret; + + ret = mt9v111_read(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_RFF_CHIP_VER, &val); + if (ret) + goto power_off; + + if ((val >> 8) != MT9V111_CHIP_ID_HIGH && + (val & 0xff) != MT9V111_CHIP_ID_LOW) { + dev_err(mt9v111->dev, + "Unable to identify MT9V111 chip: 0x%2x%2x\n", + val >> 8, val & 0xff); + ret = -EIO; + goto power_off; + } + + dev_dbg(mt9v111->dev, "Chip identified: 0x%2x%2x\n", + val >> 8, val & 0xff); + +power_off: + __mt9v111_power_off(&mt9v111->sd); + + return ret; +} + +static int mt9v111_probe(struct i2c_client *client) +{ + struct mt9v111_dev *mt9v111; + struct v4l2_fract tpf; + int ret; + + mt9v111 = devm_kzalloc(&client->dev, sizeof(*mt9v111), GFP_KERNEL); + if (!mt9v111) + return -ENOMEM; + + mt9v111->dev = &client->dev; + mt9v111->client = client; + + mt9v111->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9v111->clk)) + return PTR_ERR(mt9v111->clk); + + mt9v111->sysclk = clk_get_rate(mt9v111->clk); + if (mt9v111->sysclk > MT9V111_MAX_CLKIN) + return -EINVAL; + + mt9v111->oe = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(mt9v111->oe)) { + dev_err(&client->dev, "Unable to get GPIO \"enable\": %ld\n", + PTR_ERR(mt9v111->oe)); + return PTR_ERR(mt9v111->oe); + } + + mt9v111->standby = devm_gpiod_get_optional(&client->dev, "standby", + GPIOD_OUT_HIGH); + if (IS_ERR(mt9v111->standby)) { + dev_err(&client->dev, "Unable to get GPIO \"standby\": %ld\n", + PTR_ERR(mt9v111->standby)); + return PTR_ERR(mt9v111->standby); + } + + mt9v111->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mt9v111->reset)) { + dev_err(&client->dev, "Unable to get GPIO \"reset\": %ld\n", + PTR_ERR(mt9v111->reset)); + return PTR_ERR(mt9v111->reset); + } + + mutex_init(&mt9v111->pwr_mutex); + mutex_init(&mt9v111->stream_mutex); + + v4l2_ctrl_handler_init(&mt9v111->ctrls, 5); + + mt9v111->auto_awb = v4l2_ctrl_new_std(&mt9v111->ctrls, + &mt9v111_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, + V4L2_WHITE_BALANCE_AUTO); + if (IS_ERR_OR_NULL(mt9v111->auto_awb)) { + ret = PTR_ERR(mt9v111->auto_awb); + goto error_free_ctrls; + } + + mt9v111->auto_exp = v4l2_ctrl_new_std_menu(&mt9v111->ctrls, + &mt9v111_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, + 0, V4L2_EXPOSURE_AUTO); + if (IS_ERR_OR_NULL(mt9v111->auto_exp)) { + ret = PTR_ERR(mt9v111->auto_exp); + goto error_free_ctrls; + } + + /* Initialize timings */ + mt9v111->hblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_HBLANK, + MT9V111_CORE_R05_MIN_HBLANK, + MT9V111_CORE_R05_MAX_HBLANK, 1, + MT9V111_CORE_R05_DEF_HBLANK); + if (IS_ERR_OR_NULL(mt9v111->hblank)) { + ret = PTR_ERR(mt9v111->hblank); + goto error_free_ctrls; + } + + mt9v111->vblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_VBLANK, + MT9V111_CORE_R06_MIN_VBLANK, + MT9V111_CORE_R06_MAX_VBLANK, 1, + MT9V111_CORE_R06_DEF_VBLANK); + if (IS_ERR_OR_NULL(mt9v111->vblank)) { + ret = PTR_ERR(mt9v111->vblank); + goto error_free_ctrls; + } + + /* PIXEL_RATE is fixed: just expose it to user space. */ + v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + DIV_ROUND_CLOSEST(mt9v111->sysclk, 2), 1, + DIV_ROUND_CLOSEST(mt9v111->sysclk, 2)); + + mt9v111->sd.ctrl_handler = &mt9v111->ctrls; + + /* Start with default configuration: 640x480 UYVY. */ + mt9v111->fmt = mt9v111_def_fmt; + + /* Re-calculate blankings for 640x480@15fps. */ + mt9v111->fps = 15; + tpf.numerator = 1; + tpf.denominator = mt9v111->fps; + mt9v111_calc_frame_rate(mt9v111, &tpf); + + mt9v111->pwr_count = 0; + mt9v111->addr_space = MT9V111_R01_IFP; + mt9v111->pending = true; + + v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; + mt9v111->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + mt9v111->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&mt9v111->sd.entity, 1, &mt9v111->pad); + if (ret) + goto error_free_ctrls; +#endif + + ret = mt9v111_chip_probe(mt9v111); + if (ret) + goto error_free_ctrls; + + ret = v4l2_async_register_subdev(&mt9v111->sd); + if (ret) + goto error_free_ctrls; + + return 0; + +error_free_ctrls: + v4l2_ctrl_handler_free(&mt9v111->ctrls); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&mt9v111->sd.entity); +#endif + + mutex_destroy(&mt9v111->pwr_mutex); + mutex_destroy(&mt9v111->stream_mutex); + + return ret; +} + +static int mt9v111_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + + v4l2_async_unregister_subdev(sd); + + v4l2_ctrl_handler_free(&mt9v111->ctrls); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + + mutex_destroy(&mt9v111->pwr_mutex); + mutex_destroy(&mt9v111->stream_mutex); + + devm_gpiod_put(mt9v111->dev, mt9v111->oe); + devm_gpiod_put(mt9v111->dev, mt9v111->standby); + devm_gpiod_put(mt9v111->dev, mt9v111->reset); + + devm_clk_put(mt9v111->dev, mt9v111->clk); + + return 0; +} + +static const struct of_device_id mt9v111_of_match[] = { + { .compatible = "aptina,mt9v111", }, + { /* sentinel */ }, +}; + +static struct i2c_driver mt9v111_driver = { + .driver = { + .name = "mt9v111", + .of_match_table = mt9v111_of_match, + }, + .probe_new = mt9v111_probe, + .remove = mt9v111_remove, +}; + +module_i2c_driver(mt9v111_driver); + +MODULE_DESCRIPTION("V4L2 sensor driver for Aptina MT9V111"); +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 3ee47cad3e6955147882f1a24a9465704d8eddd2 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 3 Jul 2018 10:08:03 -0400 Subject: media: ov2680: Add Omnivision OV2680 sensor driver This patch adds V4L2 sub-device driver for OV2680 image sensor. The OV2680 is a 1/5" CMOS color sensor from Omnivision. Supports output format: 10-bit Raw RGB. The OV2680 has a single lane MIPI interface. The driver exposes following V4L2 controls: - auto/manual exposure, - exposure, - auto/manual gain, - gain, - horizontal/vertical flip, - test pattern menu. Supported resolution are only: QUXGA, 720P, UXGA. [Sakari Ailus: Drop "-level" from Kconfig help text] Signed-off-by: Rui Miguel Silva Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov2680.c | 1186 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1199 insertions(+) create mode 100644 drivers/media/i2c/ov2680.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index f4892f2b9788..82af97430e5b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -637,6 +637,18 @@ config VIDEO_OV2659 To compile this driver as a module, choose M here: the module will be called ov2659. +config VIDEO_OV2680 + tristate "OmniVision OV2680 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + ---help--- + This is a Video4Linux2 sensor driver for the OmniVision + OV2680 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2680. + config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 804d0818da18..a94eb03d10d4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c new file mode 100644 index 000000000000..f753a1c333ef --- /dev/null +++ b/drivers/media/i2c/ov2680.c @@ -0,0 +1,1186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Omnivision OV2680 CMOS Image Sensor driver + * + * Copyright (C) 2018 Linaro Ltd + * + * Based on OV5640 Sensor Driver + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2014-2017 Mentor Graphics Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OV2680_XVCLK_VALUE 24000000 + +#define OV2680_CHIP_ID 0x2680 + +#define OV2680_REG_STREAM_CTRL 0x0100 +#define OV2680_REG_SOFT_RESET 0x0103 + +#define OV2680_REG_CHIP_ID_HIGH 0x300a +#define OV2680_REG_CHIP_ID_LOW 0x300b + +#define OV2680_REG_R_MANUAL 0x3503 +#define OV2680_REG_GAIN_PK 0x350a +#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 +#define OV2680_REG_TIMING_HTS 0x380c +#define OV2680_REG_TIMING_VTS 0x380e +#define OV2680_REG_FORMAT1 0x3820 +#define OV2680_REG_FORMAT2 0x3821 + +#define OV2680_REG_ISP_CTRL00 0x5080 + +#define OV2680_FRAME_RATE 30 + +#define OV2680_REG_VALUE_8BIT 1 +#define OV2680_REG_VALUE_16BIT 2 +#define OV2680_REG_VALUE_24BIT 3 + +#define OV2680_WIDTH_MAX 1600 +#define OV2680_HEIGHT_MAX 1200 + +enum ov2680_mode_id { + OV2680_MODE_QUXGA_800_600, + OV2680_MODE_720P_1280_720, + OV2680_MODE_UXGA_1600_1200, + OV2680_MODE_MAX, +}; + +struct reg_value { + u16 reg_addr; + u8 val; +}; + +static const char * const ov2680_supply_name[] = { + "DOVDD", + "DVDD", + "AVDD", +}; + +#define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_name) + +struct ov2680_mode_info { + const char *name; + enum ov2680_mode_id id; + u32 width; + u32 height; + const struct reg_value *reg_data; + u32 reg_data_size; +}; + +struct ov2680_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + }; + struct { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *test_pattern; +}; + +struct ov2680_dev { + struct i2c_client *i2c_client; + struct v4l2_subdev sd; + + struct media_pad pad; + struct clk *xvclk; + u32 xvclk_freq; + struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; + + struct gpio_desc *reset_gpio; + struct mutex lock; /* protect members */ + + bool mode_pending_changes; + bool is_enabled; + bool is_streaming; + + struct ov2680_ctrls ctrls; + struct v4l2_mbus_framefmt fmt; + struct v4l2_fract frame_interval; + + const struct ov2680_mode_info *current_mode; +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Random Data", + "Square", + "Black Image", +}; + +static const int ov2680_hv_flip_bayer_order[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + +static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = { + {0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20}, + {0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac}, + {0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00}, + {0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0}, +}; + +static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = { + {0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, + {0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05}, + {0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11}, + {0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00}, +}; + +static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = { + {0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06}, + {0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06}, + {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00}, + {0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0}, + {0x4008, 0x00}, {0x4837, 0x18} +}; + +static const struct ov2680_mode_info ov2680_mode_init_data = { + "mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600, + ov2680_setting_30fps_QUXGA_800_600, + ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600), +}; + +static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = { + {"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, + 800, 600, ov2680_setting_30fps_QUXGA_800_600, + ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)}, + {"mode_720p_1280_720", OV2680_MODE_720P_1280_720, + 1280, 720, ov2680_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)}, + {"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200, + 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200, + ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)}, +}; + +static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov2680_dev, sd); +} + +static struct device *ov2680_to_dev(struct ov2680_dev *sensor) +{ + return &sensor->i2c_client->dev; +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov2680_dev, + ctrls.handler)->sd; +} + +static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg, + unsigned int len, u32 val) +{ + struct i2c_client *client = sensor->i2c_client; + u8 buf[6]; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) { + dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); + return -EIO; + } + + return 0; +} + +#define ov2680_write_reg(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v) + +#define ov2680_write_reg16(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v) + +#define ov2680_write_reg24(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v) + +static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msgs[2]; + u8 addr_buf[2] = { reg >> 8, reg & 0xff }; + u8 data_buf[4] = { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); + return -EIO; + } + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +#define ov2680_read_reg(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v) + +#define ov2680_read_reg16(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v) + +#define ov2680_read_reg24(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v) + +static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val) +{ + u32 readval; + int ret; + + ret = ov2680_read_reg(sensor, reg, &readval); + if (ret < 0) + return ret; + + readval &= ~mask; + val &= mask; + val |= readval; + + return ov2680_write_reg(sensor, reg, val); +} + +static int ov2680_load_regs(struct ov2680_dev *sensor, + const struct ov2680_mode_info *mode) +{ + const struct reg_value *regs = mode->reg_data; + unsigned int i; + int ret = 0; + u16 reg_addr; + u8 val; + + for (i = 0; i < mode->reg_data_size; ++i, ++regs) { + reg_addr = regs->reg_addr; + val = regs->val; + + ret = ov2680_write_reg(sensor, reg_addr, val); + if (ret) + break; + } + + return ret; +} + +static void ov2680_power_up(struct ov2680_dev *sensor) +{ + if (!sensor->reset_gpio) + return; + + gpiod_set_value(sensor->reset_gpio, 0); + usleep_range(5000, 10000); +} + +static void ov2680_power_down(struct ov2680_dev *sensor) +{ + if (!sensor->reset_gpio) + return; + + gpiod_set_value(sensor->reset_gpio, 1); + usleep_range(5000, 10000); +} + +static int ov2680_bayer_order(struct ov2680_dev *sensor) +{ + u32 format1; + u32 format2; + u32 hv_flip; + int ret; + + ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1); + if (ret < 0) + return ret; + + ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2); + if (ret < 0) + return ret; + + hv_flip = (format2 & BIT(2) << 1) | (format1 & BIT(2)); + + sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip]; + + return 0; +} + +static int ov2680_vflip_enable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_vflip_disable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_hflip_enable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_hflip_disable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) +{ + int ret; + + if (!value) + return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0); + + ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1); + if (ret < 0) + return ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); + if (ret < 0) + return ret; + + return 0; +} + +static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + u32 gain; + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1), + auto_gain ? 0 : BIT(1)); + if (ret < 0) + return ret; + + if (auto_gain || !ctrls->gain->is_new) + return 0; + + gain = ctrls->gain->val; + + ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain); + + return 0; +} + +static int ov2680_gain_get(struct ov2680_dev *sensor) +{ + u32 gain; + int ret; + + ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain); + if (ret) + return ret; + + return gain; +} + +static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + u32 exp; + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0), + auto_exp ? 0 : BIT(0)); + if (ret < 0) + return ret; + + if (auto_exp || !ctrls->exposure->is_new) + return 0; + + exp = (u32)ctrls->exposure->val; + exp <<= 4; + + return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp); +} + +static int ov2680_exposure_get(struct ov2680_dev *sensor) +{ + int ret; + u32 exp; + + ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp); + if (ret) + return ret; + + return exp >> 4; +} + +static int ov2680_stream_enable(struct ov2680_dev *sensor) +{ + return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1); +} + +static int ov2680_stream_disable(struct ov2680_dev *sensor) +{ + return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0); +} + +static int ov2680_mode_set(struct ov2680_dev *sensor) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + int ret; + + ret = ov2680_gain_set(sensor, false); + if (ret < 0) + return ret; + + ret = ov2680_exposure_set(sensor, false); + if (ret < 0) + return ret; + + ret = ov2680_load_regs(sensor, sensor->current_mode); + if (ret < 0) + return ret; + + if (ctrls->auto_gain->val) { + ret = ov2680_gain_set(sensor, true); + if (ret < 0) + return ret; + } + + if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) { + ret = ov2680_exposure_set(sensor, true); + if (ret < 0) + return ret; + } + + sensor->mode_pending_changes = false; + + return 0; +} + +static int ov2680_mode_restore(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_load_regs(sensor, &ov2680_mode_init_data); + if (ret < 0) + return ret; + + return ov2680_mode_set(sensor); +} + +static int ov2680_power_off(struct ov2680_dev *sensor) +{ + if (!sensor->is_enabled) + return 0; + + clk_disable_unprepare(sensor->xvclk); + ov2680_power_down(sensor); + regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); + sensor->is_enabled = false; + + return 0; +} + +static int ov2680_power_on(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + int ret; + + if (sensor->is_enabled) + return 0; + + ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, sensor->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + if (!sensor->reset_gpio) { + ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01); + if (ret != 0) { + dev_err(dev, "sensor soft reset failed\n"); + return ret; + } + usleep_range(1000, 2000); + } else { + ov2680_power_down(sensor); + ov2680_power_up(sensor); + } + + ret = clk_prepare_enable(sensor->xvclk); + if (ret < 0) + return ret; + + ret = ov2680_mode_restore(sensor); + if (ret < 0) + goto disable; + + sensor->is_enabled = true; + + /* Set clock lane into LP-11 state */ + ov2680_stream_enable(sensor); + usleep_range(1000, 2000); + ov2680_stream_disable(sensor); + + return 0; + +disable: + dev_err(dev, "failed to enable sensor: %d\n", ret); + ov2680_power_off(sensor); + + return ret; +} + +static int ov2680_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + if (on) + ret = ov2680_power_on(sensor); + else + ret = ov2680_power_off(sensor); + + mutex_unlock(&sensor->lock); + + if (on && ret == 0) { + ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret < 0) + return ret; + } + + return ret; +} + +static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + mutex_lock(&sensor->lock); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->is_streaming == !!enable) + goto unlock; + + if (enable && sensor->mode_pending_changes) { + ret = ov2680_mode_set(sensor); + if (ret < 0) + goto unlock; + } + + if (enable) + ret = ov2680_stream_enable(sensor); + else + ret = ov2680_stream_disable(sensor); + + sensor->is_streaming = !!enable; + +unlock: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + if (code->pad != 0 || code->index != 0) + return -EINVAL; + + code->code = sensor->fmt.code; + + return 0; +} + +static int ov2680_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *fmt = NULL; + int ret = 0; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); +#else + ret = -ENOTTY; +#endif + } else { + fmt = &sensor->fmt; + } + + if (fmt) + format->format = *fmt; + + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *try_fmt; +#endif + const struct ov2680_mode_info *mode; + int ret = 0; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (sensor->is_streaming) { + ret = -EBUSY; + goto unlock; + } + + mode = v4l2_find_nearest_size(ov2680_mode_data, + ARRAY_SIZE(ov2680_mode_data), width, + height, fmt->width, fmt->height); + if (!mode) { + ret = -EINVAL; + goto unlock; + } + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *try_fmt; +#else + ret = -ENOTTY; +#endif + + goto unlock; + } + + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = sensor->fmt.code; + fmt->colorspace = sensor->fmt.colorspace; + + sensor->current_mode = mode; + sensor->fmt = format->format; + sensor->mode_pending_changes = true; + +unlock: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .width = 800, + .height = 600, + } + }; + + return ov2680_set_fmt(sd, cfg, &fmt); +} + +static int ov2680_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + int index = fse->index; + + if (index >= OV2680_MODE_MAX || index < 0) + return -EINVAL; + + fse->min_width = ov2680_mode_data[index].width; + fse->min_height = ov2680_mode_data[index].height; + fse->max_width = ov2680_mode_data[index].width; + fse->max_height = ov2680_mode_data[index].height; + + return 0; +} + +static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct v4l2_fract tpf; + + if (fie->index >= OV2680_MODE_MAX || fie->width > OV2680_WIDTH_MAX || + fie->height > OV2680_HEIGHT_MAX || + fie->which > V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + tpf.denominator = OV2680_FRAME_RATE; + tpf.numerator = 1; + + fie->interval = tpf; + + return 0; +} + +static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct ov2680_ctrls *ctrls = &sensor->ctrls; + int val; + + if (!sensor->is_enabled) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + val = ov2680_gain_get(sensor); + if (val < 0) + return val; + ctrls->gain->val = val; + break; + case V4L2_CID_EXPOSURE: + val = ov2680_exposure_get(sensor); + if (val < 0) + return val; + ctrls->exposure->val = val; + break; + } + + return 0; +} + +static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct ov2680_ctrls *ctrls = &sensor->ctrls; + + if (!sensor->is_enabled) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + return ov2680_gain_set(sensor, !!ctrl->val); + case V4L2_CID_GAIN: + return ov2680_gain_set(sensor, !!ctrls->auto_gain->val); + case V4L2_CID_EXPOSURE_AUTO: + return ov2680_exposure_set(sensor, !!ctrl->val); + case V4L2_CID_EXPOSURE: + return ov2680_exposure_set(sensor, !!ctrls->auto_exp->val); + case V4L2_CID_VFLIP: + if (sensor->is_streaming) + return -EBUSY; + if (ctrl->val) + return ov2680_vflip_enable(sensor); + else + return ov2680_vflip_disable(sensor); + case V4L2_CID_HFLIP: + if (sensor->is_streaming) + return -EBUSY; + if (ctrl->val) + return ov2680_hflip_enable(sensor); + else + return ov2680_hflip_disable(sensor); + case V4L2_CID_TEST_PATTERN: + return ov2680_test_pattern_set(sensor, ctrl->val); + default: + break; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { + .g_volatile_ctrl = ov2680_g_volatile_ctrl, + .s_ctrl = ov2680_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops ov2680_core_ops = { + .s_power = ov2680_s_power, +}; + +static const struct v4l2_subdev_video_ops ov2680_video_ops = { + .g_frame_interval = ov2680_s_g_frame_interval, + .s_frame_interval = ov2680_s_g_frame_interval, + .s_stream = ov2680_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { + .init_cfg = ov2680_init_cfg, + .enum_mbus_code = ov2680_enum_mbus_code, + .get_fmt = ov2680_get_fmt, + .set_fmt = ov2680_set_fmt, + .enum_frame_size = ov2680_enum_frame_size, + .enum_frame_interval = ov2680_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops ov2680_subdev_ops = { + .core = &ov2680_core_ops, + .video = &ov2680_video_ops, + .pad = &ov2680_pad_ops, +}; + +static int ov2680_mode_init(struct ov2680_dev *sensor) +{ + const struct ov2680_mode_info *init_mode; + + /* set initial mode */ + sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; + sensor->fmt.width = 800; + sensor->fmt.height = 600; + sensor->fmt.field = V4L2_FIELD_NONE; + sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB; + + sensor->frame_interval.denominator = OV2680_FRAME_RATE; + sensor->frame_interval.numerator = 1; + + init_mode = &ov2680_mode_init_data; + + sensor->current_mode = init_mode; + + sensor->mode_pending_changes = true; + + return 0; +} + +static int ov2680_v4l2_init(struct ov2680_dev *sensor) +{ + const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; + struct ov2680_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret = 0; + + v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client, + &ov2680_subdev_ops); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(hdl, 7); + + hdl->lock = &sensor->lock; + + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + + ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, + &ov2680_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 32767, 1, 0); + + ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, + 0, 1, 1, 1); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0); + + if (hdl->error) { + ret = hdl->error; + goto cleanup_entity; + } + + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + + sensor->sd.ctrl_handler = hdl; + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret < 0) + goto cleanup_entity; + + return 0; + +cleanup_entity: + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +static int ov2680_get_regulators(struct ov2680_dev *sensor) +{ + int i; + + for (i = 0; i < OV2680_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = ov2680_supply_name[i]; + + return devm_regulator_bulk_get(&sensor->i2c_client->dev, + OV2680_NUM_SUPPLIES, + sensor->supplies); +} + +static int ov2680_check_id(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + u32 chip_id; + int ret; + + ov2680_power_on(sensor); + + ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id); + if (ret < 0) { + dev_err(dev, "failed to read chip id high\n"); + return -ENODEV; + } + + if (chip_id != OV2680_CHIP_ID) { + dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n", + chip_id, OV2680_CHIP_ID); + return -ENODEV; + } + + return 0; +} + +static int ov2860_parse_dt(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + int ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(sensor->reset_gpio); + if (ret < 0) { + dev_dbg(dev, "error while getting reset gpio: %d\n", ret); + return ret; + } + + sensor->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(sensor->xvclk)) { + dev_err(dev, "xvclk clock missing or invalid\n"); + return PTR_ERR(sensor->xvclk); + } + + sensor->xvclk_freq = clk_get_rate(sensor->xvclk); + if (sensor->xvclk_freq != OV2680_XVCLK_VALUE) { + dev_err(dev, "wrong xvclk frequency %d HZ, expected: %d Hz\n", + sensor->xvclk_freq, OV2680_XVCLK_VALUE); + return -EINVAL; + } + + return 0; +} + +static int ov2680_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ov2680_dev *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->i2c_client = client; + + ret = ov2860_parse_dt(sensor); + if (ret < 0) + return -EINVAL; + + ret = ov2680_mode_init(sensor); + if (ret < 0) + return ret; + + ret = ov2680_get_regulators(sensor); + if (ret < 0) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + mutex_init(&sensor->lock); + + ret = ov2680_v4l2_init(sensor); + if (ret < 0) + goto lock_destroy; + + ret = ov2680_check_id(sensor); + if (ret < 0) + goto error_cleanup; + + dev_info(dev, "ov2680 init correctly\n"); + + return 0; + +error_cleanup: + dev_err(dev, "ov2680 init fail: %d\n", ret); + + media_entity_cleanup(&sensor->sd.entity); + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + +lock_destroy: + mutex_destroy(&sensor->lock); + + return ret; +} + +static int ov2680_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + mutex_destroy(&sensor->lock); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + + return 0; +} + +static int __maybe_unused ov2680_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + if (sensor->is_streaming) + ov2680_stream_disable(sensor); + + return 0; +} + +static int __maybe_unused ov2680_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret; + + if (sensor->is_streaming) { + ret = ov2680_stream_enable(sensor); + if (ret < 0) + goto stream_disable; + } + + return 0; + +stream_disable: + ov2680_stream_disable(sensor); + sensor->is_streaming = false; + + return ret; +} + +static const struct dev_pm_ops ov2680_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume) +}; + +static const struct of_device_id ov2680_dt_ids[] = { + { .compatible = "ovti,ov2680" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov2680_dt_ids); + +static struct i2c_driver ov2680_i2c_driver = { + .driver = { + .name = "ov2680", + .pm = &ov2680_pm_ops, + .of_match_table = of_match_ptr(ov2680_dt_ids), + }, + .probe_new = ov2680_probe, + .remove = ov2680_remove, +}; +module_i2c_driver(ov2680_i2c_driver); + +MODULE_AUTHOR("Rui Miguel Silva "); +MODULE_DESCRIPTION("OV2680 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 279b4b9aaa3d04ea8de960e39e54e38aafe93e62 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 25 Jul 2018 12:24:54 -0400 Subject: media: imx274: use regmap_bulk_write to write multybyte registers Currently 2-bytes and 3-bytes registers are set by very similar functions doing the needed shift & mask manipulation, followed by very similar for loops setting one byte at a time over I2C. Replace all of this code by a unique helper function that calls regmap_bulk_write(), which has two advantages: - sets all the bytes in a unique I2C transaction - removes lots of now unused code. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 102 +++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 63 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index edca63eaea9b..093c4587832a 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -690,6 +690,35 @@ static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) return err; } +/** + * Write a multibyte register. + * + * Uses a bulk write where possible. + * + * @priv: Pointer to device structure + * @addr: Address of the LSB register. Other registers must be + * consecutive, least-to-most significant. + * @val: Value to be written to the register (cpu endianness) + * @nbytes: Number of bits to write (range: [1..3]) + */ +static int imx274_write_mbreg(struct stimx274 *priv, u16 addr, u32 val, + size_t nbytes) +{ + __le32 val_le = cpu_to_le32(val); + int err; + + err = regmap_bulk_write(priv->regmap, addr, &val_le, nbytes); + if (err) + dev_err(&priv->client->dev, + "%s : i2c bulk write failed, %x = %x (%zu bytes)\n", + __func__, addr, val, nbytes); + else + dev_dbg(&priv->client->dev, + "%s : addr 0x%x, val=0x%x (%zu bytes)\n", + __func__, addr, val, nbytes); + return err; +} + /* * Set mode registers to start stream. * @priv: Pointer to device structure @@ -1163,15 +1192,6 @@ static int imx274_set_digital_gain(struct stimx274 *priv, u32 dgain) reg_val & IMX274_MASK_LSB_4_BITS); } -static inline void imx274_calculate_gain_regs(struct reg_8 regs[2], u16 gain) -{ - regs->addr = IMX274_ANALOG_GAIN_ADDR_MSB; - regs->val = (gain >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_3_BITS; - - (regs + 1)->addr = IMX274_ANALOG_GAIN_ADDR_LSB; - (regs + 1)->val = (gain) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_gain - Function called when setting gain * @priv: Pointer to device structure @@ -1185,10 +1205,8 @@ static inline void imx274_calculate_gain_regs(struct reg_8 regs[2], u16 gain) */ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl) { - struct reg_8 reg_list[2]; int err; u32 gain, analog_gain, digital_gain, gain_reg; - int i; gain = (u32)(ctrl->val); @@ -1229,14 +1247,10 @@ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl) if (gain_reg > IMX274_GAIN_REG_MAX) gain_reg = IMX274_GAIN_REG_MAX; - imx274_calculate_gain_regs(reg_list, (u16)gain_reg); - - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_ANALOG_GAIN_ADDR_LSB, gain_reg, + 2); + if (err) + goto fail; if (IMX274_GAIN_CONST - gain_reg == 0) { err = -EINVAL; @@ -1258,16 +1272,6 @@ fail: return err; } -static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], - u32 coarse_time) -{ - regs->addr = IMX274_SHR_REG_MSB; - regs->val = (coarse_time >> IMX274_SHIFT_8_BITS) - & IMX274_MASK_LSB_8_BITS; - (regs + 1)->addr = IMX274_SHR_REG_LSB; - (regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_coarse_time - Function called when setting SHR value * @priv: Pointer to device structure @@ -1279,10 +1283,8 @@ static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], */ static int imx274_set_coarse_time(struct stimx274 *priv, u32 *val) { - struct reg_8 reg_list[2]; int err; u32 coarse_time, frame_length; - int i; coarse_time = *val; @@ -1291,16 +1293,9 @@ static int imx274_set_coarse_time(struct stimx274 *priv, u32 *val) if (err) goto fail; - /* prepare SHR registers */ - imx274_calculate_coarse_time_regs(reg_list, coarse_time); - - /* write to SHR registers */ - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_SHR_REG_LSB, coarse_time, 2); + if (err) + goto fail; *val = frame_length - coarse_time; return 0; @@ -1429,19 +1424,6 @@ static int imx274_set_test_pattern(struct stimx274 *priv, int val) return err; } -static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], - u32 frame_length) -{ - regs->addr = IMX274_VMAX_REG_1; - regs->val = (frame_length >> IMX274_SHIFT_16_BITS) - & IMX274_MASK_LSB_4_BITS; - (regs + 1)->addr = IMX274_VMAX_REG_2; - (regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS) - & IMX274_MASK_LSB_8_BITS; - (regs + 2)->addr = IMX274_VMAX_REG_3; - (regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_frame_length - Function called when setting frame length * @priv: Pointer to device structure @@ -1453,23 +1435,17 @@ static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], */ static int imx274_set_frame_length(struct stimx274 *priv, u32 val) { - struct reg_8 reg_list[3]; int err; u32 frame_length; - int i; dev_dbg(&priv->client->dev, "%s : input length = %d\n", __func__, val); frame_length = (u32)val; - imx274_calculate_frame_length_regs(reg_list, frame_length); - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_VMAX_REG_3, frame_length, 3); + if (err) + goto fail; return 0; -- cgit v1.2.3-70-g09d2 From 39dd23dc9d4c512954ec511d22d93ea854cf0265 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 25 Jul 2018 12:24:55 -0400 Subject: media: imx274: add cropping support via SELECTION API Currently this driver does not support cropping. The supported modes are the following, all capturing the entire area: - 3840x2160, 1:1 binning (native sensor resolution) - 1920x1080, 2:1 binning - 1280x720, 3:1 binning The VIDIOC_SUBDEV_S_FMT ioctl chooses among these 3 configurations the one that matches the requested format. Add cropping support via VIDIOC_SUBDEV_S_SELECTION: with target V4L2_SEL_TGT_CROP to choose the captured area, with V4L2_SEL_TGT_COMPOSE to choose the output resolution. To maintain backward compatibility we also allow setting the compose format via VIDIOC_SUBDEV_S_FMT. To obtain this, compose rect and output format are computed in the common helper function __imx274_change_compose(), which sets both to the same width/height values. Cropping also calls __imx274_change_compose() whenever cropping rect size changes in order to reset the compose rect (and output format size) for 1:1 binning. Also rename enum imx274_mode to imx274_binning (and its values from IMX274_MODE_BINNING_* to IMX274_BINNING_*). Without cropping, the two naming are equivalent. With cropping, the resolution could be different, e.g. using 2:1 binning mode to crop 1200x960 and output a 600x480 format. Using binning in the names avoids any misunderstanding. For the same reason, replace the 'size' field in struct imx274_frmfmt with 'bin_ratio'. [Sakari Ailus: Remove leftover condition in imx274_apply_trimming] Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 466 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 375 insertions(+), 91 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 093c4587832a..f8c70f1a34fe 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -5,6 +5,7 @@ * * Leon Luo * Edwin Zou + * Luca Ceresoli * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +76,7 @@ */ #define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72) -#define IMX274_DEFAULT_MODE IMX274_MODE_3840X2160 +#define IMX274_DEFAULT_MODE IMX274_BINNING_OFF #define IMX274_MAX_WIDTH (3840) #define IMX274_MAX_HEIGHT (2160) #define IMX274_MAX_FRAME_RATE (120) @@ -111,6 +113,20 @@ #define IMX274_SHR_REG_LSB 0x300C /* SHR */ #define IMX274_SVR_REG_MSB 0x300F /* SVR */ #define IMX274_SVR_REG_LSB 0x300E /* SVR */ +#define IMX274_HTRIM_EN_REG 0x3037 +#define IMX274_HTRIM_START_REG_LSB 0x3038 +#define IMX274_HTRIM_START_REG_MSB 0x3039 +#define IMX274_HTRIM_END_REG_LSB 0x303A +#define IMX274_HTRIM_END_REG_MSB 0x303B +#define IMX274_VWIDCUTEN_REG 0x30DD +#define IMX274_VWIDCUT_REG_LSB 0x30DE +#define IMX274_VWIDCUT_REG_MSB 0x30DF +#define IMX274_VWINPOS_REG_LSB 0x30E0 +#define IMX274_VWINPOS_REG_MSB 0x30E1 +#define IMX274_WRITE_VSIZE_REG_LSB 0x3130 +#define IMX274_WRITE_VSIZE_REG_MSB 0x3131 +#define IMX274_Y_OUT_SIZE_REG_LSB 0x3132 +#define IMX274_Y_OUT_SIZE_REG_MSB 0x3133 #define IMX274_VMAX_REG_1 0x30FA /* VMAX, MSB */ #define IMX274_VMAX_REG_2 0x30F9 /* VMAX */ #define IMX274_VMAX_REG_3 0x30F8 /* VMAX, LSB */ @@ -140,10 +156,10 @@ static const struct regmap_config imx274_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -enum imx274_mode { - IMX274_MODE_3840X2160, - IMX274_MODE_1920X1080, - IMX274_MODE_1280X720, +enum imx274_binning { + IMX274_BINNING_OFF, + IMX274_BINNING_2_1, + IMX274_BINNING_3_1, }; /* @@ -152,8 +168,8 @@ enum imx274_mode { * These are the values to configure the sensor in one of the * implemented modes. * - * @size: recommended recording pixels * @init_regs: registers to initialize the mode + * @bin_ratio: downscale factor (e.g. 3 for 3:1 binning) * @min_frame_len: Minimum frame length for each mode (see "Frame Rate * Adjustment (CSI-2)" in the datasheet) * @min_SHR: Minimum SHR register value (see "Shutter Setting (CSI-2)" in the @@ -163,8 +179,8 @@ enum imx274_mode { * in Each Readout Drive Mode (CSI-2)" in the datasheet) */ struct imx274_frmfmt { - struct v4l2_frmsize_discrete size; const struct reg_8 *init_regs; + unsigned int bin_ratio; int min_frame_len; int min_SHR; int max_fps; @@ -215,31 +231,14 @@ static const struct reg_8 imx274_mode1_3840x2160_raw10[] = { {0x3004, 0x01}, {0x3005, 0x01}, {0x3006, 0x00}, - {0x3007, 0x02}, + {0x3007, 0xa2}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x01}, - {0x30F6, 0x07}, /* HMAX, 263 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30dd, 0x01}, /* crop to 2160 */ - {0x30de, 0x06}, - {0x30df, 0x00}, - {0x30e0, 0x12}, - {0x30e1, 0x00}, - {0x3037, 0x01}, /* to crop to 3840 */ - {0x3038, 0x0c}, - {0x3039, 0x00}, - {0x303a, 0x0c}, - {0x303b, 0x0f}, {0x30EE, 0x01}, - {0x3130, 0x86}, - {0x3131, 0x08}, - {0x3132, 0x7E}, - {0x3133, 0x08}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x16}, @@ -273,32 +272,14 @@ static const struct reg_8 imx274_mode3_1920x1080_raw10[] = { {0x3004, 0x02}, {0x3005, 0x21}, {0x3006, 0x00}, - {0x3007, 0x11}, + {0x3007, 0xb1}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x02}, - {0x30F6, 0x04}, /* HMAX, 260 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30dd, 0x01}, /* to crop to 1920x1080 */ - {0x30de, 0x05}, - {0x30df, 0x00}, - {0x30e0, 0x04}, - {0x30e1, 0x00}, - {0x3037, 0x01}, - {0x3038, 0x0c}, - {0x3039, 0x00}, - {0x303a, 0x0c}, - {0x303b, 0x0f}, - {0x30EE, 0x01}, - {0x3130, 0x4E}, - {0x3131, 0x04}, - {0x3132, 0x46}, - {0x3133, 0x04}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x1A}, @@ -331,31 +312,14 @@ static const struct reg_8 imx274_mode5_1280x720_raw10[] = { {0x3004, 0x03}, {0x3005, 0x31}, {0x3006, 0x00}, - {0x3007, 0x09}, + {0x3007, 0xa9}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x03}, - {0x30F6, 0x04}, /* HMAX, 260 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30DD, 0x01}, - {0x30DE, 0x07}, - {0x30DF, 0x00}, - {0x40E0, 0x04}, - {0x30E1, 0x00}, - {0x3030, 0xD4}, - {0x3031, 0x02}, - {0x3032, 0xD0}, - {0x3033, 0x02}, - {0x30EE, 0x01}, - {0x3130, 0xE2}, - {0x3131, 0x02}, - {0x3132, 0xDE}, - {0x3133, 0x02}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x1B}, @@ -498,7 +462,7 @@ static const struct reg_8 imx274_tp_regs[] = { static const struct imx274_frmfmt imx274_formats[] = { { /* mode 1, 4K */ - .size = {3840, 2160}, + .bin_ratio = 1, .init_regs = imx274_mode1_3840x2160_raw10, .min_frame_len = 4550, .min_SHR = 12, @@ -507,7 +471,7 @@ static const struct imx274_frmfmt imx274_formats[] = { }, { /* mode 3, 1080p */ - .size = {1920, 1080}, + .bin_ratio = 2, .init_regs = imx274_mode3_1920x1080_raw10, .min_frame_len = 2310, .min_SHR = 8, @@ -516,7 +480,7 @@ static const struct imx274_frmfmt imx274_formats[] = { }, { /* mode 5, 720p */ - .size = {1280, 720}, + .bin_ratio = 3, .init_regs = imx274_mode5_1280x720_raw10, .min_frame_len = 2310, .min_SHR = 8, @@ -547,7 +511,10 @@ struct imx274_ctrls { * @pad: Media pad structure * @client: Pointer to I2C client * @ctrls: imx274 control structure + * @crop: rect to be captured + * @compose: compose rect, i.e. output resolution * @format: V4L2 media bus frame format structure + * (width and height are in sync with the compose rect) * @frame_rate: V4L2 frame rate structure * @regmap: Pointer to regmap structure * @reset_gpio: Pointer to reset gpio @@ -559,6 +526,7 @@ struct stimx274 { struct media_pad pad; struct i2c_client *client; struct imx274_ctrls ctrls; + struct v4l2_rect crop; struct v4l2_mbus_framefmt format; struct v4l2_fract frame_interval; struct regmap *regmap; @@ -567,6 +535,13 @@ struct stimx274 { const struct imx274_frmfmt *mode; }; +#define IMX274_ROUND(dim, step, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? roundup((dim), (step)) \ + : ((flags) & V4L2_SEL_FLAG_LE \ + ? rounddown((dim), (step)) \ + : rounddown((dim) + (step) / 2, (step)))) + /* * Function declaration */ @@ -840,6 +815,114 @@ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) return ret; } +static int imx274_binning_goodness(struct stimx274 *imx274, + int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct device *dev = &imx274->client->dev; + const int goodness = 100000; + int val = 0; + + if (flags & V4L2_SEL_FLAG_GE) { + if (w < ask_w) + val -= goodness; + if (h < ask_h) + val -= goodness; + } + + if (flags & V4L2_SEL_FLAG_LE) { + if (w > ask_w) + val -= goodness; + if (h > ask_h) + val -= goodness; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + dev_dbg(dev, "%s: ask %dx%d, size %dx%d, goodness %d\n", + __func__, ask_w, ask_h, w, h, val); + + return val; +} + +/** + * Helper function to change binning and set both compose and format. + * + * We have two entry points to change binning: set_fmt and + * set_selection(COMPOSE). Both have to compute the new output size + * and set it in both the compose rect and the frame format size. We + * also need to do the same things after setting cropping to restore + * 1:1 binning. + * + * This function contains the common code for these three cases, it + * has many arguments in order to accommodate the needs of all of + * them. + * + * Must be called with imx274->lock locked. + * + * @imx274: The device object + * @cfg: The pad config we are editing for TRY requests + * @which: V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY from the caller + * @width: Input-output parameter: set to the desired width before + * the call, contains the chosen value after returning successfully + * @height: Input-output parameter for height (see @width) + * @flags: Selection flags from struct v4l2_subdev_selection, or 0 if not + * available (when called from set_fmt) + */ +static int __imx274_change_compose(struct stimx274 *imx274, + struct v4l2_subdev_pad_config *cfg, + u32 which, + u32 *width, + u32 *height, + u32 flags) +{ + struct device *dev = &imx274->client->dev; + const struct v4l2_rect *cur_crop; + struct v4l2_mbus_framefmt *tgt_fmt; + unsigned int i; + const struct imx274_frmfmt *best_mode = &imx274_formats[0]; + int best_goodness = INT_MIN; + + if (which == V4L2_SUBDEV_FORMAT_TRY) { + cur_crop = &cfg->try_crop; + tgt_fmt = &cfg->try_fmt; + } else { + cur_crop = &imx274->crop; + tgt_fmt = &imx274->format; + } + + for (i = 0; i < ARRAY_SIZE(imx274_formats); i++) { + unsigned int ratio = imx274_formats[i].bin_ratio; + + int goodness = imx274_binning_goodness( + imx274, + cur_crop->width / ratio, *width, + cur_crop->height / ratio, *height, + flags); + + if (goodness >= best_goodness) { + best_goodness = goodness; + best_mode = &imx274_formats[i]; + } + } + + *width = cur_crop->width / best_mode->bin_ratio; + *height = cur_crop->height / best_mode->bin_ratio; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + imx274->mode = best_mode; + + dev_dbg(dev, "%s: selected %u:1 binning\n", + __func__, best_mode->bin_ratio); + + tgt_fmt->width = *width; + tgt_fmt->height = *height; + tgt_fmt->field = V4L2_FIELD_NONE; + + return 0; +} + /** * imx274_get_fmt - Get the pad format * @sd: Pointer to V4L2 Sub device structure @@ -878,45 +961,238 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt = &format->format; struct stimx274 *imx274 = to_imx274(sd); - struct i2c_client *client = imx274->client; - int index; - - dev_dbg(&client->dev, - "%s: width = %d height = %d code = %d\n", - __func__, fmt->width, fmt->height, fmt->code); + int err = 0; mutex_lock(&imx274->lock); - for (index = 0; index < ARRAY_SIZE(imx274_formats); index++) { - if (imx274_formats[index].size.width == fmt->width && - imx274_formats[index].size.height == fmt->height) - break; - } - - if (index >= ARRAY_SIZE(imx274_formats)) { - /* default to first format */ - index = 0; - } + err = __imx274_change_compose(imx274, cfg, format->which, + &fmt->width, &fmt->height, 0); - imx274->mode = &imx274_formats[index]; + if (err) + goto out; - if (fmt->width > IMX274_MAX_WIDTH) - fmt->width = IMX274_MAX_WIDTH; - if (fmt->height > IMX274_MAX_HEIGHT) - fmt->height = IMX274_MAX_HEIGHT; - fmt->width = fmt->width & (~IMX274_MASK_LSB_2_BITS); - fmt->height = fmt->height & (~IMX274_MASK_LSB_2_BITS); + /* + * __imx274_change_compose already set width and height in the + * applicable format, but we need to keep all other format + * values, so do a full copy here + */ fmt->field = V4L2_FIELD_NONE; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) cfg->try_fmt = *fmt; else imx274->format = *fmt; +out: + mutex_unlock(&imx274->lock); + + return err; +} + +static int imx274_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct stimx274 *imx274 = to_imx274(sd); + const struct v4l2_rect *src_crop; + const struct v4l2_mbus_framefmt *src_fmt; + int ret = 0; + + if (sel->pad != 0) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX274_MAX_WIDTH; + sel->r.height = IMX274_MAX_HEIGHT; + return 0; + } + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + src_crop = &cfg->try_crop; + src_fmt = &cfg->try_fmt; + } else { + src_crop = &imx274->crop; + src_fmt = &imx274->format; + } + + mutex_lock(&imx274->lock); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *src_crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = src_crop->width; + sel->r.height = src_crop->height; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = src_fmt->width; + sel->r.height = src_fmt->height; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&imx274->lock); + + return ret; +} + +static int imx274_set_selection_crop(struct stimx274 *imx274, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_rect *tgt_crop; + struct v4l2_rect new_crop; + bool size_changed; + + /* + * h_step could be 12 or 24 depending on the binning. But we + * won't know the binning until we choose the mode later in + * __imx274_change_compose(). Thus let's be safe and use the + * most conservative value in all cases. + */ + const u32 h_step = 24; + + new_crop.width = min_t(u32, + IMX274_ROUND(sel->r.width, h_step, sel->flags), + IMX274_MAX_WIDTH); + + /* Constraint: HTRIMMING_END - HTRIMMING_START >= 144 */ + if (new_crop.width < 144) + new_crop.width = 144; + + new_crop.left = min_t(u32, + IMX274_ROUND(sel->r.left, h_step, 0), + IMX274_MAX_WIDTH - new_crop.width); + + new_crop.height = min_t(u32, + IMX274_ROUND(sel->r.height, 2, sel->flags), + IMX274_MAX_HEIGHT); + + new_crop.top = min_t(u32, IMX274_ROUND(sel->r.top, 2, 0), + IMX274_MAX_HEIGHT - new_crop.height); + + sel->r = new_crop; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + tgt_crop = &cfg->try_crop; + else + tgt_crop = &imx274->crop; + + mutex_lock(&imx274->lock); + + size_changed = (new_crop.width != tgt_crop->width || + new_crop.height != tgt_crop->height); + + /* __imx274_change_compose needs the new size in *tgt_crop */ + *tgt_crop = new_crop; + + /* if crop size changed then reset the output image size */ + if (size_changed) + __imx274_change_compose(imx274, cfg, sel->which, + &new_crop.width, &new_crop.height, + sel->flags); + mutex_unlock(&imx274->lock); + return 0; } +static int imx274_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct stimx274 *imx274 = to_imx274(sd); + + if (sel->pad != 0) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP) + return imx274_set_selection_crop(imx274, cfg, sel); + + if (sel->target == V4L2_SEL_TGT_COMPOSE) { + int err; + + mutex_lock(&imx274->lock); + err = __imx274_change_compose(imx274, cfg, sel->which, + &sel->r.width, &sel->r.height, + sel->flags); + mutex_unlock(&imx274->lock); + + /* + * __imx274_change_compose already set width and + * height in set->r, we still need to set top-left + */ + if (!err) { + sel->r.top = 0; + sel->r.left = 0; + } + + return err; + } + + return -EINVAL; +} + +static int imx274_apply_trimming(struct stimx274 *imx274) +{ + u32 h_start; + u32 h_end; + u32 hmax; + u32 v_cut; + s32 v_pos; + u32 write_v_size; + u32 y_out_size; + int err; + + h_start = imx274->crop.left + 12; + h_end = h_start + imx274->crop.width; + + /* Use the minimum allowed value of HMAX */ + /* Note: except in mode 1, (width / 16 + 23) is always < hmax_min */ + /* Note: 260 is the minimum HMAX in all implemented modes */ + hmax = max_t(u32, 260, (imx274->crop.width) / 16 + 23); + + /* invert v_pos if VFLIP */ + v_pos = imx274->ctrls.vflip->cur.val ? + (-imx274->crop.top / 2) : (imx274->crop.top / 2); + v_cut = (IMX274_MAX_HEIGHT - imx274->crop.height) / 2; + write_v_size = imx274->crop.height + 22; + y_out_size = imx274->crop.height + 14; + + err = imx274_write_mbreg(imx274, IMX274_HMAX_REG_LSB, hmax, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_EN_REG, 1, 1); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_START_REG_LSB, + h_start, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_END_REG_LSB, + h_end, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWIDCUTEN_REG, 1, 1); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWIDCUT_REG_LSB, + v_cut, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWINPOS_REG_LSB, + v_pos, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_WRITE_VSIZE_REG_LSB, + write_v_size, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_Y_OUT_SIZE_REG_LSB, + y_out_size, 2); + + return err; +} + /** * imx274_g_frame_interval - Get the frame interval * @sd: Pointer to V4L2 Sub device structure @@ -1057,6 +1333,10 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) if (ret) goto fail; + ret = imx274_apply_trimming(imx274); + if (ret) + goto fail; + /* * update frame rate & expsoure. if the last mode is different, * HMAX could be changed. As the result, frame rate & exposure @@ -1545,6 +1825,8 @@ fail: static const struct v4l2_subdev_pad_ops imx274_pad_ops = { .get_fmt = imx274_get_fmt, .set_fmt = imx274_set_fmt, + .get_selection = imx274_get_selection, + .set_selection = imx274_set_selection, }; static const struct v4l2_subdev_video_ops imx274_video_ops = { @@ -1590,8 +1872,10 @@ static int imx274_probe(struct i2c_client *client, /* initialize format */ imx274->mode = &imx274_formats[IMX274_DEFAULT_MODE]; - imx274->format.width = imx274->mode->size.width; - imx274->format.height = imx274->mode->size.height; + imx274->crop.width = IMX274_MAX_WIDTH; + imx274->crop.height = IMX274_MAX_HEIGHT; + imx274->format.width = imx274->crop.width / imx274->mode->bin_ratio; + imx274->format.height = imx274->crop.height / imx274->mode->bin_ratio; imx274->format.field = V4L2_FIELD_NONE; imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; imx274->format.colorspace = V4L2_COLORSPACE_SRGB; -- cgit v1.2.3-70-g09d2 From 9db3bbf58be59ab496ccd1c7528f4b0e8835c5b1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 25 Jul 2018 13:15:12 -0400 Subject: media: v4l2-mem2mem: Fix missing v4l2_m2m_try_run call Commit 34dbb848d5e4 ("media: mem2mem: Remove excessive try_run call") removed a redundant call to v4l2_m2m_try_run but instead introduced a bug. Consider the following case: 1) Context A schedules, queues and runs job A. 2) While the m2m device is running, context B schedules and queues job B. Job B cannot run, because it has to wait for job A. 3) Job A completes, calls v4l2_m2m_job_finish, and tries to queue a job for context A, but since the context is empty it won't do anything. In this scenario, queued job B will never run. Fix this by calling v4l2_m2m_try_run from v4l2_m2m_try_schedule. While here, add more documentation to these functions. Fixes: 34dbb848d5e4 ("media: mem2mem: Remove excessive try_run call") Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: split >80 cols line] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index d9565ee44578..0a93c5b173c2 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -249,15 +249,24 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Running job on m2m_ctx: %p\n", m2m_dev->curr_ctx); m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); } -void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +/* + * __v4l2_m2m_try_queue() - queue a job + * @m2m_dev: m2m device + * @m2m_ctx: m2m context + * + * Check if this context is ready to queue a job. + * + * This function can run in interrupt context. + */ +static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) { - struct v4l2_m2m_dev *m2m_dev; unsigned long flags_job, flags_out, flags_cap; - m2m_dev = m2m_ctx->m2m_dev; dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); if (!m2m_ctx->out_q_ctx.q.streaming @@ -315,7 +324,25 @@ void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) m2m_ctx->job_flags |= TRANS_QUEUED; spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); +} + +/** + * v4l2_m2m_try_schedule() - schedule and possibly run a job for any context + * @m2m_ctx: m2m context + * + * Check if this context is ready to queue a job. If suitable, + * run the next queued job on the mem2mem device. + * + * This function shouldn't run in interrupt context. + * + * Note that v4l2_m2m_try_schedule() can schedule one job for this context, + * and then run another job for another context. + */ +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev = m2m_ctx->m2m_dev; + __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); v4l2_m2m_try_run(m2m_dev); } EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule); -- cgit v1.2.3-70-g09d2 From 4faeaf9c0f4581667ce5826f9c90c4fd463ef086 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 5 Jun 2018 09:33:59 -0400 Subject: media: s5p-mfc: Fix buffer look up in s5p_mfc_handle_frame_{new, copy_time} functions Look up of buffers in s5p_mfc_handle_frame_new, s5p_mfc_handle_frame_copy_time functions is not working properly for DMA addresses above 2 GiB. As a result flags and timestamp of returned buffers are not set correctly and it breaks operation of GStreamer/OMX plugins which rely on the CAPTURE buffer queue flags. Due to improper return type of the get_dec_y_adr, get_dspl_y_adr callbacks and sign bit extension these callbacks return incorrect address values, e.g. 0xfffffffffefc0000 instead of 0x00000000fefc0000. Then the statement: "if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) == dec_y_addr)" is always false, which breaks looking up capture queue buffers. To ensure proper matching by address u32 type is used for the DMA addresses. This should work on all related SoCs, since the MFC DMA address width is not larger than 32-bit. Changes done in this patch are minimal as there is a larger patch series pending refactoring the whole driver. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 9ca707cb2a42..927a1235408d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -254,24 +254,24 @@ static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx) static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - struct s5p_mfc_buf *dst_buf, *src_buf; - size_t dec_y_addr; + struct s5p_mfc_buf *dst_buf, *src_buf; + u32 dec_y_addr; unsigned int frame_type; /* Make sure we actually have a new frame before continuing. */ frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) return; - dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev); + dec_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev); /* Copy timestamp / timecode from decoded src to dst and set appropriate flags. */ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) - == dec_y_addr) { - dst_buf->b->timecode = - src_buf->b->timecode; + u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); + + if (addr == dec_y_addr) { + dst_buf->b->timecode = src_buf->b->timecode; dst_buf->b->vb2_buf.timestamp = src_buf->b->vb2_buf.timestamp; dst_buf->b->flags &= @@ -307,10 +307,10 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_buf; - size_t dspl_y_addr; + u32 dspl_y_addr; unsigned int frame_type; - dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); + dspl_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); if (IS_MFCV6_PLUS(dev)) frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx); @@ -329,9 +329,10 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) /* The MFC returns address of the buffer, now we have to * check which videobuf does it correspond to */ list_for_each_entry(dst_buf, &ctx->dst_queue, list) { + u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); + /* Check if this is the buffer we're looking for */ - if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) - == dspl_y_addr) { + if (addr == dspl_y_addr) { list_del(&dst_buf->list); ctx->dst_queue_cnt--; dst_buf->b->sequence = ctx->sequence; -- cgit v1.2.3-70-g09d2 From b30cc07de8a903685441f9770b1b21e1422d2468 Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Sun, 10 Jun 2018 10:45:31 -0400 Subject: media: dvb-usb/friio, dvb-usb-v2/gl861: decompose friio and merge with gl861 Friio device contains "gl861" bridge and "tc90522" demod, for which the separate drivers are already in the kernel. But friio driver was monolithic and did not use them, practically copying those features. This patch decomposes friio driver into sub drivers and re-uses existing ones, thus reduces some code. It adds some features to gl861, to support the friio-specific init/config of the devices and implement i2c communications to the tuner via demod with USB vendor requests. [mchehab+samsung@kernel.org: fix merge conflicts] Signed-off-by: Akihiro Tsukada Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Kconfig | 5 +- drivers/media/usb/dvb-usb-v2/gl861.c | 471 +++++++++++++++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/gl861.h | 1 + drivers/media/usb/dvb-usb/Kconfig | 6 - drivers/media/usb/dvb-usb/Makefile | 3 - 5 files changed, 476 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 082b8d67244b..df4412245a8a 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -96,10 +96,13 @@ config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB_V2 select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 - receiver with USB ID 0db0:5581. + receiver with USB ID 0db0:5581, Friio White ISDB-T receiver + with USB ID 0x7a69:0001. config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index fee4b30df778..3338b21d8b25 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -6,10 +6,14 @@ * * see Documentation/media/dvb-drivers/dvb-usb.rst for more information */ +#include + #include "gl861.h" #include "zl10353.h" #include "qt1010.h" +#include "tc90522.h" +#include "dvb-pll.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -161,11 +165,478 @@ static struct dvb_usb_device_properties gl861_props = { } }; + +/* + * For Friio + */ + +struct friio_priv { + struct i2c_adapter *demod_sub_i2c; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + struct i2c_adapter tuner_adap; +}; + +struct friio_config { + struct i2c_board_info demod_info; + struct tc90522_config demod_cfg; + + struct i2c_board_info tuner_info; + struct dvb_pll_config tuner_cfg; +}; + +static const struct friio_config friio_config = { + .demod_info = { I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x18), }, + .tuner_info = { I2C_BOARD_INFO("tua6034_friio", 0x60), }, +}; + +/* For another type of I2C: + * message sent by a USB control-read/write transaction with data stage. + * Used in init/config of Friio. + */ +static int +gl861_i2c_write_ex(struct dvb_usb_device *d, u8 addr, u8 *wbuf, u16 wlen) +{ + u8 *buf; + int ret; + + buf = kmalloc(wlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, wbuf, wlen); + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_RAW, GL861_WRITE, + addr << (8 + 1), 0x0100, buf, wlen, 2000); + kfree(buf); + return ret; +} + +static int +gl861_i2c_read_ex(struct dvb_usb_device *d, u8 addr, u8 *rbuf, u16 rlen) +{ + u8 *buf; + int ret; + + buf = kmalloc(rlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + GL861_REQ_I2C_READ, GL861_READ, + addr << (8 + 1), 0x0100, buf, rlen, 2000); + if (ret > 0 && rlen > 0) + memcpy(buf, rbuf, rlen); + kfree(buf); + return ret; +} + +/* For I2C transactions to the tuner of Friio (dvb_pll). + * + * Friio uses irregular USB encapsulation for tuner i2c transactions: + * write transacions are encapsulated with a different USB 'request' value. + * + * Although all transactions are sent via the demod(tc90522) + * and the demod provides an i2c adapter for them, it cannot be used in Friio + * since it assumes using the same parent adapter with the demod, + * which does not use the request value and uses same one for both read/write. + * So we define a dedicated i2c adapter here. + */ + +static int +friio_i2c_tuner_read(struct dvb_usb_device *d, struct i2c_msg *msg) +{ + struct friio_priv *priv; + u8 addr; + + priv = d_to_priv(d); + addr = priv->i2c_client_demod->addr; + return gl861_i2c_read_ex(d, addr, msg->buf, msg->len); +} + +static int +friio_i2c_tuner_write(struct dvb_usb_device *d, struct i2c_msg *msg) +{ + u8 *buf; + int ret; + struct friio_priv *priv; + + priv = d_to_priv(d); + + if (msg->len < 1) + return -EINVAL; + + buf = kmalloc(msg->len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + buf[0] = msg->addr << 1; + memcpy(buf + 1, msg->buf, msg->len); + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_RAW, GL861_WRITE, + priv->i2c_client_demod->addr << (8 + 1), + 0xFE, buf, msg->len + 1, 2000); + kfree(buf); + return ret; +} + +static int friio_tuner_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + int ret; + + if (msg[i].flags & I2C_M_RD) + ret = friio_i2c_tuner_read(d, &msg[i]); + else + ret = friio_i2c_tuner_write(d, &msg[i]); + + if (ret < 0) + break; + + usleep_range(1000, 2000); /* avoid I2C errors */ + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static struct i2c_algorithm friio_tuner_i2c_algo = { + .master_xfer = friio_tuner_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +/* GPIO control in Friio */ + +#define FRIIO_CTL_LNB (1 << 0) +#define FRIIO_CTL_STROBE (1 << 1) +#define FRIIO_CTL_CLK (1 << 2) +#define FRIIO_CTL_LED (1 << 3) + +#define FRIIO_LED_RUNNING 0x6400ff64 +#define FRIIO_LED_STOPPED 0x96ff00ff + +/* control PIC16F676 attached to Friio */ +static int friio_ext_ctl(struct dvb_usb_device *d, + u32 sat_color, int power_on) +{ + int i, ret; + struct i2c_msg msg; + u8 *buf; + u32 mask; + u8 power = (power_on) ? FRIIO_CTL_LNB : 0; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.addr = 0x00; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + buf[0] = 0x00; + + /* send 2bit header (&B10) */ + buf[1] = power | FRIIO_CTL_LED | FRIIO_CTL_STROBE; + ret = i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + buf[1] = power | FRIIO_CTL_STROBE; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + /* send 32bit(satur, R, G, B) data in serial */ + mask = 1 << 31; + for (i = 0; i < 32; i++) { + buf[1] = power | FRIIO_CTL_STROBE; + if (sat_color & mask) + buf[1] |= FRIIO_CTL_LED; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + mask >>= 1; + } + + /* set the strobe off */ + buf[1] = power; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + kfree(buf); + return (ret == 70) ? 0 : -EREMOTEIO; +} + +/* init/config of gl861 for Friio */ +/* NOTE: + * This function cannot be moved to friio_init()/dvb_usbv2_init(), + * because the init defined here must be done before any activities like I2C, + * but friio_init() is called by dvb-usbv2 after {_frontend, _tuner}_attach(), + * where I2C communication is used. + * Thus this function is set to be called from _power_ctl(). + * + * Since it will be called on the early init stage + * where the i2c adapter is not initialized yet, + * we cannot use i2c_transfer() here. + */ +static int friio_reset(struct dvb_usb_device *d) +{ + int i, ret; + u8 wbuf[2], rbuf[2]; + + static const u8 friio_init_cmds[][2] = { + {0x33, 0x08}, {0x37, 0x40}, {0x3a, 0x1f}, {0x3b, 0xff}, + {0x3c, 0x1f}, {0x3d, 0xff}, {0x38, 0x00}, {0x35, 0x00}, + {0x39, 0x00}, {0x36, 0x00}, + }; + + ret = usb_set_interface(d->udev, 0, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x11; + wbuf[1] = 0x02; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + usleep_range(2000, 3000); + + wbuf[0] = 0x11; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + /* + * Check if the dev is really a Friio White, since it might be + * another device, Friio Black, with the same VID/PID. + */ + + usleep_range(1000, 2000); + wbuf[0] = 0x03; + wbuf[1] = 0x80; + ret = gl861_i2c_write_ex(d, 0x09, wbuf, 2); + if (ret < 0) + return ret; + + usleep_range(2000, 3000); + ret = gl861_i2c_read_ex(d, 0x09, rbuf, 2); + if (ret < 0) + return ret; + if (rbuf[0] != 0xff || rbuf[1] != 0xff) + return -ENODEV; + + + usleep_range(1000, 2000); + ret = gl861_i2c_write_ex(d, 0x48, wbuf, 2); + if (ret < 0) + return ret; + + usleep_range(2000, 3000); + ret = gl861_i2c_read_ex(d, 0x48, rbuf, 2); + if (ret < 0) + return ret; + if (rbuf[0] != 0xff || rbuf[1] != 0xff) + return -ENODEV; + + wbuf[0] = 0x30; + wbuf[1] = 0x04; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x00; + wbuf[1] = 0x01; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x06; + wbuf[1] = 0x0f; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(friio_init_cmds); i++) { + ret = gl861_i2c_msg(d, 0x00, (u8 *)friio_init_cmds[i], 2, + NULL, 0); + if (ret < 0) + return ret; + } + return 0; +} + +/* + * DVB callbacks for Friio + */ + +static int friio_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + return onoff ? friio_reset(d) : 0; +} + +static int friio_frontend_attach(struct dvb_usb_adapter *adap) +{ + const struct i2c_board_info *info; + struct dvb_usb_device *d; + struct tc90522_config cfg; + struct i2c_client *cl; + struct friio_priv *priv; + + info = &friio_config.demod_info; + d = adap_to_d(adap); + cl = dvb_module_probe("tc90522", info->type, + &d->i2c_adap, info->addr, &cfg); + if (!cl) + return -ENODEV; + adap->fe[0] = cfg.fe; + + /* ignore cfg.tuner_i2c and create new one */ + priv = adap_to_priv(adap); + priv->i2c_client_demod = cl; + priv->tuner_adap.algo = &friio_tuner_i2c_algo; + priv->tuner_adap.dev.parent = &d->udev->dev; + strlcpy(priv->tuner_adap.name, d->name, sizeof(priv->tuner_adap.name)); + strlcat(priv->tuner_adap.name, "-tuner", sizeof(priv->tuner_adap.name)); + priv->demod_sub_i2c = &priv->tuner_adap; + i2c_set_adapdata(&priv->tuner_adap, d); + + return i2c_add_adapter(&priv->tuner_adap); +} + +static int friio_frontend_detach(struct dvb_usb_adapter *adap) +{ + struct friio_priv *priv; + + priv = adap_to_priv(adap); + i2c_del_adapter(&priv->tuner_adap); + dvb_module_release(priv->i2c_client_demod); + return 0; +} + +static int friio_tuner_attach(struct dvb_usb_adapter *adap) +{ + const struct i2c_board_info *info; + struct dvb_pll_config cfg; + struct i2c_client *cl; + struct friio_priv *priv; + + priv = adap_to_priv(adap); + info = &friio_config.tuner_info; + cfg = friio_config.tuner_cfg; + cfg.fe = adap->fe[0]; + + cl = dvb_module_probe("dvb_pll", info->type, + priv->demod_sub_i2c, info->addr, &cfg); + if (!cl) + return -ENODEV; + priv->i2c_client_tuner = cl; + return 0; +} + +static int friio_tuner_detach(struct dvb_usb_adapter *adap) +{ + struct friio_priv *priv; + + priv = adap_to_priv(adap); + dvb_module_release(priv->i2c_client_tuner); + return 0; +} + +static int friio_init(struct dvb_usb_device *d) +{ + int i; + int ret; + struct friio_priv *priv; + + static const u8 demod_init[][2] = { + {0x01, 0x40}, {0x04, 0x38}, {0x05, 0x40}, {0x07, 0x40}, + {0x0f, 0x4f}, {0x11, 0x21}, {0x12, 0x0b}, {0x13, 0x2f}, + {0x14, 0x31}, {0x16, 0x02}, {0x21, 0xc4}, {0x22, 0x20}, + {0x2c, 0x79}, {0x2d, 0x34}, {0x2f, 0x00}, {0x30, 0x28}, + {0x31, 0x31}, {0x32, 0xdf}, {0x38, 0x01}, {0x39, 0x78}, + {0x3b, 0x33}, {0x3c, 0x33}, {0x48, 0x90}, {0x51, 0x68}, + {0x5e, 0x38}, {0x71, 0x00}, {0x72, 0x08}, {0x77, 0x00}, + {0xc0, 0x21}, {0xc1, 0x10}, {0xe4, 0x1a}, {0xea, 0x1f}, + {0x77, 0x00}, {0x71, 0x00}, {0x71, 0x00}, {0x76, 0x0c}, + }; + + /* power on LNA? */ + ret = friio_ext_ctl(d, FRIIO_LED_STOPPED, true); + if (ret < 0) + return ret; + msleep(20); + + /* init/config demod */ + priv = d_to_priv(d); + for (i = 0; i < ARRAY_SIZE(demod_init); i++) { + int ret; + + ret = i2c_master_send(priv->i2c_client_demod, demod_init[i], 2); + if (ret < 0) + return ret; + } + msleep(100); + return 0; +} + +static void friio_exit(struct dvb_usb_device *d) +{ + friio_ext_ctl(d, FRIIO_LED_STOPPED, false); +} + +static int friio_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + u32 led_color; + + led_color = onoff ? FRIIO_LED_RUNNING : FRIIO_LED_STOPPED; + return friio_ext_ctl(fe_to_d(fe), led_color, true); +} + + +static struct dvb_usb_device_properties friio_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + + .size_of_priv = sizeof(struct friio_priv), + + .i2c_algo = &gl861_i2c_algo, + .power_ctrl = friio_power_ctrl, + .frontend_attach = friio_frontend_attach, + .frontend_detach = friio_frontend_detach, + .tuner_attach = friio_tuner_attach, + .tuner_detach = friio_tuner_detach, + .init = friio_init, + .exit = friio_exit, + .streaming_ctrl = friio_streaming_ctrl, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x01, 8, 16384), + } + } +}; + static const struct usb_device_id gl861_id_table[] = { { DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801, &gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU, &gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) }, + { DVB_USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE, + &friio_props, "774 Friio White ISDB-T USB2.0", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, gl861_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/gl861.h b/drivers/media/usb/dvb-usb-v2/gl861.h index b651b857e034..02c00e10748a 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.h +++ b/drivers/media/usb/dvb-usb-v2/gl861.h @@ -9,5 +9,6 @@ #define GL861_REQ_I2C_WRITE 0x01 #define GL861_REQ_I2C_READ 0x02 +#define GL861_REQ_I2C_RAW 0x03 #endif diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index b8a1c62a0682..513df955eaa3 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -312,12 +312,6 @@ config DVB_USB_DTV5100 help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. -config DVB_USB_FRIIO - tristate "Friio ISDB-T USB2.0 Receiver support" - depends on DVB_USB - help - Say Y here to support the Japanese DTV receiver Friio. - config DVB_USB_AZ6027 tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 9ad2618408ef..407d90ca8be0 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -71,9 +71,6 @@ obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o -dvb-usb-friio-objs := friio.o friio-fe.o -obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o - dvb-usb-az6027-objs := az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o -- cgit v1.2.3-70-g09d2 From f917fc0e9b9988982a841b871655cc41bde1e10e Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Sun, 10 Jun 2018 10:49:15 -0400 Subject: media: dvb-frontends/dvb-pll: fix module ref-counting dvb-pll module was 'put' twice on exit: once by dvb_frontend_detach() and another by dvb_module_release(). Signed-off-by: Akihiro Tsukada Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/dvb-pll.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index e3894ff403d7..4a663420190e 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -884,6 +884,17 @@ dvb_pll_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id)) return -ENOMEM; + /* + * Unset tuner_ops.release (== dvb_pll_release) + * which has been just set in the above dvb_pll_attach(), + * because if tuner_ops.release was left defined, + * this module would be 'put' twice on exit: + * once by dvb_frontend_detach() and another by dvb_module_release(). + * + * dvb_pll_release is instead executed in the i2c driver's .remove(), + * keeping dvb_pll_attach untouched for legacy (dvb_attach) drivers. + */ + fe->ops.tuner_ops.release = NULL; dev_info(&client->dev, "DVB Simple Tuner attached.\n"); return 0; } -- cgit v1.2.3-70-g09d2 From 44e2971f9e50aadb8d62eafbe31df014ef55d618 Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Sun, 10 Jun 2018 10:58:02 -0400 Subject: media: pci/pt1: suppress compiler warning in xtensa arch Found and reported by kbuild test robot: Message ID: <201805052003.MC007f9h%fengguang.wu@intel.com> and holding an address of an empty struct in .driver.pm does no harm when CONFIG_PM_SLEEP is not defined. Signed-off-by: Akihiro Tsukada Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/pt1/pt1.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c index fda969a85684..7f878fc41b7e 100644 --- a/drivers/media/pci/pt1/pt1.c +++ b/drivers/media/pci/pt1/pt1.c @@ -1443,9 +1443,7 @@ static struct pci_driver pt1_driver = { .probe = pt1_probe, .remove = pt1_remove, .id_table = pt1_id_table, -#ifdef CONFIG_PM_SLEEP .driver.pm = &pt1_pm_ops, -#endif }; module_pci_driver(pt1_driver); -- cgit v1.2.3-70-g09d2 From 157eb9a0b75e97ad390c6e50c7381b0a0e02fe97 Mon Sep 17 00:00:00 2001 From: Robert Schlabbach Date: Sat, 16 Jun 2018 15:04:22 -0400 Subject: media: em28xx: explicitly disable TS packet filter The em28xx driver never touched the EM2874 register bits that control the transport stream packet filters, leaving them at whatever default the firmware has set. E.g. the Pinnacle 290e disables them by default, while the Hauppauge WinTV dualHD enables discarding NULL packets by default. However, some applications require NULL packets, e.g. to determine the load in DOCSIS segments, so discarding NULL packets is undesired for such applications. This patch simply extends the bit mask when starting or stopping the transport stream packet capture, so that the filter bits are cleared. It has been verified that this makes the Hauppauge WinTV dualHD pass an unfiltered DVB-C stream including NULL packets, which it didn't before. Signed-off-by: Robert Schlabbach Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index f70845e7d8c6..45b24776a695 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -655,12 +655,12 @@ int em28xx_capture_start(struct em28xx *dev, int start) rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE); + EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD); else rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, - EM2874_TS2_CAPTURE_ENABLE); + EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD); } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ -- cgit v1.2.3-70-g09d2 From f9dc3af8212b626a62103e7ba8cb3c84482334b8 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:35:58 -0400 Subject: media: dvb-frontends/stv0910: cast the BER denominator shift exp to ULL To avoid miscalculations related to the BER denominator, the shift expression needs to be casted as ULL. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0910.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index 41444fa1c0bb..91b21eb59531 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -682,8 +682,8 @@ static int get_bit_error_rate_s(struct stv *state, u32 *bernumerator, return -EINVAL; if ((regs[0] & 0x80) == 0) { - state->last_berdenominator = 1 << ((state->berscale * 2) + - 10 + 3); + state->last_berdenominator = 1ULL << ((state->berscale * 2) + + 10 + 3); state->last_bernumerator = ((u32)(regs[0] & 0x7F) << 16) | ((u32)regs[1] << 8) | regs[2]; if (state->last_bernumerator < 256 && state->berscale < 6) { -- cgit v1.2.3-70-g09d2 From d33be4327eea80b46701d2b71f15749ec793f92d Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:35:59 -0400 Subject: media: ddbridge: probe for LNBH25 chips before attaching In demod_attach_stv0910(), the LNBH25 IC is being blindly attached and, if the result is bad, blindly attached on another possible I2C address. The LNBH25 uses it's set_voltage function to test for the IC and will print an error to the kernel log on failure. Prevent this by probing the possible I2C address and use this (and only this) to attach the LNBH25 I2C driver. This also allows the stv0910 attach function to be a bit cleaner. Picked up from the upstream dddvb GIT and adapted for the LNBH25 driver variant from the kernel tree. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-core.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index d5b0d1eaf3ad..3f83415b06c7 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1191,6 +1191,13 @@ static const struct lnbh25_config lnbh25_cfg = { .data2_config = LNBH25_TEN }; +static int has_lnbh25(struct i2c_adapter *i2c, u8 adr) +{ + u8 val; + + return i2c_read_reg(i2c, adr, 0, &val) ? 0 : 1; +} + static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast) { struct i2c_adapter *i2c = &input->port->i2c->adap; @@ -1224,14 +1231,15 @@ static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast) /* attach lnbh25 - leftshift by one as the lnbh25 driver expects 8bit * i2c addresses */ - lnbcfg.i2c_address = (((input->nr & 1) ? 0x0d : 0x0c) << 1); - if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { + if (has_lnbh25(i2c, 0x0d)) + lnbcfg.i2c_address = (((input->nr & 1) ? 0x0d : 0x0c) << 1); + else lnbcfg.i2c_address = (((input->nr & 1) ? 0x09 : 0x08) << 1); - if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { - dev_err(dev, "No LNBH25 found!\n"); - dvb_frontend_detach(dvb->fe); - return -ENODEV; - } + + if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { + dev_err(dev, "No LNBH25 found!\n"); + dvb_frontend_detach(dvb->fe); + return -ENODEV; } return 0; -- cgit v1.2.3-70-g09d2 From ce2280fbe65f14ab13663e2174abe2575b977c32 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:00 -0400 Subject: media: ddbridge: evaluate the actual link when setting up the dummy tuner Devices supporting dummy tuner operation can exist on any link, not only on link 0, so fix this accordingly. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 3f83415b06c7..55eb151f329e 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1859,7 +1859,7 @@ static void ddb_port_probe(struct ddb_port *port) /* Handle missing ports and ports without I2C */ if (dummy_tuner && !port->nr && - dev->link[0].ids.device == 0x0005) { + dev->link[l].ids.device == 0x0005) { port->name = "DUMMY"; port->class = DDB_PORT_TUNER; port->type = DDB_TUNER_DUMMY; -- cgit v1.2.3-70-g09d2 From d96eeee502cc5c75e0ab7d61468a3b906e609e98 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:01 -0400 Subject: media: ddbridge: report I2C bus errors The I2C_COMMAND response reports an error in the I2C bus communication using bit 17. Evaluate the response more thoroughly and log an error if an I2C problem was detected. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c index 667340c86ea7..5a28d7611713 100644 --- a/drivers/media/pci/ddbridge/ddbridge-i2c.c +++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c @@ -73,7 +73,10 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) } return -EIO; } - if (val & 0x70000) + val &= 0x70000; + if (val == 0x20000) + dev_err(dev->dev, "I2C bus error\n"); + if (val) return -EIO; return 0; } -- cgit v1.2.3-70-g09d2 From 7b6b9b153ad0127ec031438e3538acdebd677b02 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:02 -0400 Subject: media: ddbridge: remove unused MDIO defines and hwinfo member ddbridge has a few MDIO related remainders (defines, hwinfo struct) which aren't of any use for the in-kernel driver at all (they're only used in conjunction with the OctoNet SAT>IP boxes which the kernel driver doesn't have any support for), so clean this up. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-regs.h | 8 -------- drivers/media/pci/ddbridge/ddbridge.h | 1 - 2 files changed, 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h index b978b5991940..f9e1cbb99b53 100644 --- a/drivers/media/pci/ddbridge/ddbridge-regs.h +++ b/drivers/media/pci/ddbridge/ddbridge-regs.h @@ -33,14 +33,6 @@ #define GPIO_INPUT 0x24 #define GPIO_DIRECTION 0x28 -/* ------------------------------------------------------------------------- */ -/* MDIO */ - -#define MDIO_CTRL 0x20 -#define MDIO_ADR 0x24 -#define MDIO_REG 0x28 -#define MDIO_VAL 0x2C - /* ------------------------------------------------------------------------- */ #define BOARD_CONTROL 0x30 diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index a66b1125cc74..9c645bee428a 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -127,7 +127,6 @@ struct ddb_info { u8 temp_bus; u32 board_control; u32 board_control_2; - u8 mdio_num; u8 con_clock; /* use a continuous clock */ u8 ts_quirks; #define TS_QUIRK_SERIAL 1 -- cgit v1.2.3-70-g09d2 From 2957e53e57f2d8a791d5d56978e194440844f186 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:03 -0400 Subject: media: ddbridge: link structure access cosmetics in ddb_port_probe() Throughout the function, dev->link[l] is used several times. Unclutter this a bit by declaring a ddb_link var at the top of the function, assign the address of dev->link[l] to it and use that var to access the link[] struct member. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 55eb151f329e..408460be00b7 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1850,6 +1850,7 @@ static void ddb_port_probe(struct ddb_port *port) { struct ddb *dev = port->dev; u32 l = port->lnr; + struct ddb_link *link = &dev->link[l]; u8 id, type; port->name = "NO MODULE"; @@ -1859,7 +1860,7 @@ static void ddb_port_probe(struct ddb_port *port) /* Handle missing ports and ports without I2C */ if (dummy_tuner && !port->nr && - dev->link[l].ids.device == 0x0005) { + link->ids.device == 0x0005) { port->name = "DUMMY"; port->class = DDB_PORT_TUNER; port->type = DDB_TUNER_DUMMY; @@ -1873,14 +1874,14 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (port->nr == 1 && dev->link[l].info->type == DDB_OCTOPUS_CI && - dev->link[l].info->i2c_mask == 1) { + if (port->nr == 1 && link->info->type == DDB_OCTOPUS_CI && + link->info->i2c_mask == 1) { port->name = "NO TAB"; port->class = DDB_PORT_NONE; return; } - if (dev->link[l].info->type == DDB_OCTOPUS_MAX) { + if (link->info->type == DDB_OCTOPUS_MAX) { port->name = "DUAL DVB-S2 MAX"; port->type_name = "MXL5XX"; port->class = DDB_PORT_TUNER; @@ -1891,8 +1892,8 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (dev->link[l].info->type == DDB_OCTOPUS_MCI) { - if (port->nr >= dev->link[l].info->mci) + if (link->info->type == DDB_OCTOPUS_MCI) { + if (port->nr >= link->info->mci) return; port->name = "DUAL MCI"; port->type_name = "MCI"; @@ -1901,7 +1902,7 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (port->nr > 1 && dev->link[l].info->type == DDB_OCTOPUS_CI) { + if (port->nr > 1 && link->info->type == DDB_OCTOPUS_CI) { port->name = "CI internal"; port->type_name = "INTERNAL"; port->class = DDB_PORT_CI; @@ -1986,7 +1987,7 @@ static void ddb_port_probe(struct ddb_port *port) port->class = DDB_PORT_TUNER; if (id == 0x51) { if (port->nr == 0 && - dev->link[l].info->ts_quirks & TS_QUIRK_REVERSED) + link->info->ts_quirks & TS_QUIRK_REVERSED) port->type = DDB_TUNER_DVBS_STV0910_PR; else port->type = DDB_TUNER_DVBS_STV0910_P; -- cgit v1.2.3-70-g09d2 From 9f269f1fd2397fe716468579b81a9cd0b2fed2ab Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:04 -0400 Subject: media: ddbridge: change MCI base ID and define a SX8 ID Change the start of the MCI ID range (internally used only) to 48 and define an ID for the SX8 card type. Use this new ID to handle device attachment. This change is done in preparation for support of more MCI based cards. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-core.c | 4 ++-- drivers/media/pci/ddbridge/ddbridge.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 408460be00b7..68ea0ffdad2d 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1592,7 +1592,7 @@ static int dvb_input_attach(struct ddb_input *input) if (demod_attach_dummy(input) < 0) goto err_detach; break; - case DDB_TUNER_MCI: + case DDB_TUNER_MCI_SX8: if (ddb_fe_attach_mci(input) < 0) goto err_detach; break; @@ -1898,7 +1898,7 @@ static void ddb_port_probe(struct ddb_port *port) port->name = "DUAL MCI"; port->type_name = "MCI"; port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_MCI; + port->type = DDB_TUNER_MCI_SX8; return; } diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 9c645bee428a..65bd74d86ed5 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -254,7 +254,6 @@ struct ddb_port { #define DDB_CI_EXTERNAL_XO2_B 13 #define DDB_TUNER_DVBS_STV0910_PR 14 #define DDB_TUNER_DVBC2T2I_SONY_P 15 -#define DDB_TUNER_MCI 16 #define DDB_TUNER_XO2 32 #define DDB_TUNER_DVBS_STV0910 (DDB_TUNER_XO2 + 0) @@ -264,6 +263,9 @@ struct ddb_port { #define DDB_TUNER_ATSC_ST (DDB_TUNER_XO2 + 4) #define DDB_TUNER_DVBC2T2I_SONY (DDB_TUNER_XO2 + 5) +#define DDB_TUNER_MCI 48 +#define DDB_TUNER_MCI_SX8 (DDB_TUNER_MCI + 0) + struct ddb_input *input[2]; struct ddb_output *output; struct dvb_ca_en50221 *en; -- cgit v1.2.3-70-g09d2 From 3addf0fa820b3da34e0c0e532167afcee8089a3b Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:05 -0400 Subject: media: ddbridge/mci: update copyright year in headers Update the copyright year information in the MCI headers to 2017-2018. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.c | 6 +++--- drivers/media/pci/ddbridge/ddbridge-mci.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index 4ac634fc96e4..46b20b06e2a6 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -2,9 +2,9 @@ /* * ddbridge-mci.c: Digital Devices microcode interface * - * Copyright (C) 2017 Digital Devices GmbH - * Ralph Metzler - * Marcus Metzler + * Copyright (C) 2017-2018 Digital Devices GmbH + * Ralph Metzler + * Marcus Metzler * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 209cc2b92dff..389f6376603b 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -2,9 +2,9 @@ /* * ddbridge-mci.h: Digital Devices micro code interface * - * Copyright (C) 2017 Digital Devices GmbH - * Marcus Metzler - * Ralph Metzler + * Copyright (C) 2017-2018 Digital Devices GmbH + * Marcus Metzler + * Ralph Metzler * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License -- cgit v1.2.3-70-g09d2 From 3c7d591121c3f5cb97abb9c5dd2a9c2bd8532674 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:06 -0400 Subject: media: ddbridge/mci: read and report signal strength and SNR Implement querying signal statistics from the MCI and report this data in read_status() as DVBv5 statistics. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.c | 47 ++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index 46b20b06e2a6..7d402861fa9e 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -155,6 +155,47 @@ static void release(struct dvb_frontend *fe) kfree(state); } +static int get_info(struct dvb_frontend *fe) +{ + int stat; + struct mci *state = fe->demodulator_priv; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = MCI_CMD_GETSIGNALINFO; + cmd.demod = state->demod; + stat = mci_cmd(state, &cmd, &state->signal_info); + return stat; +} + +static int get_snr(struct dvb_frontend *fe) +{ + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = + (s64)state->signal_info.dvbs2_signal_info.signal_to_noise + * 10; + return 0; +} + +static int get_strength(struct dvb_frontend *fe) +{ + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 str; + + str = 100000 - + (state->signal_info.dvbs2_signal_info.channel_power + * 10 + 108750); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = str; + return 0; +} + static int read_status(struct dvb_frontend *fe, enum fe_status *status) { int stat; @@ -168,10 +209,14 @@ static int read_status(struct dvb_frontend *fe, enum fe_status *status) if (stat) return stat; *status = 0x00; + get_info(fe); + get_strength(fe); if (res.status == SX8_DEMOD_WAIT_MATYPE) *status = 0x0f; - if (res.status == SX8_DEMOD_LOCKED) + if (res.status == SX8_DEMOD_LOCKED) { *status = 0x1f; + get_snr(fe); + } return stat; } -- cgit v1.2.3-70-g09d2 From 9b28ba016fc4a387ffefccbc6c390af3fdf37eb9 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:07 -0400 Subject: media: ddbridge/mci: rename defines and fix i/q var types Adjustments to match the FPGA firmware, and the signal I/Q values are reported as s16 types from the card firmware. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.c | 2 +- drivers/media/pci/ddbridge/ddbridge-mci.h | 56 +++++++++++++++---------------- 2 files changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index 7d402861fa9e..fa0d7d0cc6f6 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -342,7 +342,7 @@ unlock: memset(&cmd, 0, sizeof(cmd)); if (state->base->iq_mode) { - cmd.command = SX8_CMD_SELECT_IQOUT; + cmd.command = SX8_CMD_ENABLE_IQOUTPUT; cmd.demod = state->demod; cmd.output = 0; mci_cmd(state, &cmd, NULL); diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 389f6376603b..2e74f0544717 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -51,40 +51,40 @@ #define SX8_TSCONFIG_BURSTSIZE_8K (0x00000020) #define SX8_TSCONFIG_BURSTSIZE_16K (0x00000030) -#define SX8_DEMOD_STOPPED (0) -#define SX8_DEMOD_IQ_MODE (1) -#define SX8_DEMOD_WAIT_SIGNAL (2) -#define SX8_DEMOD_WAIT_MATYPE (3) -#define SX8_DEMOD_TIMEOUT (14) -#define SX8_DEMOD_LOCKED (15) +#define SX8_DEMOD_STOPPED (0) +#define SX8_DEMOD_IQ_MODE (1) +#define SX8_DEMOD_WAIT_SIGNAL (2) +#define SX8_DEMOD_WAIT_MATYPE (3) +#define SX8_DEMOD_TIMEOUT (14) +#define SX8_DEMOD_LOCKED (15) -#define MCI_CMD_STOP (0x01) -#define MCI_CMD_GETSTATUS (0x02) -#define MCI_CMD_GETSIGNALINFO (0x03) -#define MCI_CMD_RFPOWER (0x04) +#define MCI_CMD_STOP (0x01) +#define MCI_CMD_GETSTATUS (0x02) +#define MCI_CMD_GETSIGNALINFO (0x03) +#define MCI_CMD_RFPOWER (0x04) -#define MCI_CMD_SEARCH_DVBS (0x10) +#define MCI_CMD_SEARCH_DVBS (0x10) -#define MCI_CMD_GET_IQSYMBOL (0x30) +#define MCI_CMD_GET_IQSYMBOL (0x30) -#define SX8_CMD_INPUT_ENABLE (0x40) -#define SX8_CMD_INPUT_DISABLE (0x41) -#define SX8_CMD_START_IQ (0x42) -#define SX8_CMD_STOP_IQ (0x43) -#define SX8_CMD_SELECT_IQOUT (0x44) -#define SX8_CMD_SELECT_TSOUT (0x45) +#define SX8_CMD_INPUT_ENABLE (0x40) +#define SX8_CMD_INPUT_DISABLE (0x41) +#define SX8_CMD_START_IQ (0x42) +#define SX8_CMD_STOP_IQ (0x43) +#define SX8_CMD_ENABLE_IQOUTPUT (0x44) +#define SX8_CMD_DISABLE_IQOUTPUT (0x45) -#define SX8_ERROR_UNSUPPORTED (0x80) +#define MCI_ERROR_UNSUPPORTED (0x80) -#define SX8_SUCCESS(status) (status < SX8_ERROR_UNSUPPORTED) +#define MCI_SUCCESS(status) (status < MCI_ERROR_UNSUPPORTED) -#define SX8_CMD_DIAG_READ8 (0xE0) -#define SX8_CMD_DIAG_READ32 (0xE1) -#define SX8_CMD_DIAG_WRITE8 (0xE2) -#define SX8_CMD_DIAG_WRITE32 (0xE3) +#define SX8_CMD_DIAG_READ8 (0xE0) +#define SX8_CMD_DIAG_READ32 (0xE1) +#define SX8_CMD_DIAG_WRITE8 (0xE2) +#define SX8_CMD_DIAG_WRITE32 (0xE3) -#define SX8_CMD_DIAG_READRF (0xE8) -#define SX8_CMD_DIAG_WRITERF (0xE9) +#define SX8_CMD_DIAG_READRF (0xE8) +#define SX8_CMD_DIAG_WRITERF (0xE9) struct mci_command { union { @@ -141,8 +141,8 @@ struct mci_result { u32 ber_denominator; } dvbs2_signal_info; struct { - u8 i_symbol; - u8 q_symbol; + s16 i; + s16 q; } dvbs2_signal_iq; }; u32 version[4]; -- cgit v1.2.3-70-g09d2 From 6094cbed4e7bee130f8b29c9a174bc336d19990b Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:08 -0400 Subject: media: ddbridge/mci: extend mci_command and mci_result structs Recent FPGA firmware reports more data and values in sent command responses. Adjust the mci_command and mci_result structs including it's unions to match these changes and add a few comments explaining things. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.h | 74 +++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 2e74f0544717..5e0c9e88b6fc 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -90,16 +90,30 @@ struct mci_command { union { u32 command_word; struct { - u8 command; - u8 tuner; - u8 demod; - u8 output; + u8 command; + u8 tuner; + u8 demod; + u8 output; }; }; union { u32 params[31]; struct { + /* + * Bit 0: DVB-S Enabled + * Bit 1: DVB-S2 Enabled + * Bit 7: InputStreamID + */ u8 flags; + /* + * Bit 0: QPSK, + * Bit 1: 8PSK/8APSK + * Bit 2: 16APSK + * Bit 3: 32APSK + * Bit 4: 64APSK + * Bit 5: 128APSK + * Bit 6: 256APSK + */ u8 s2_modulation_mask; u8 rsvd1; u8 retry; @@ -108,7 +122,36 @@ struct mci_command { u8 input_stream_id; u8 rsvd2[3]; u32 scrambling_sequence_index; + u32 frequency_range; } dvbs2_search; + + struct { + u8 tap; + u8 rsvd; + u16 point; + } get_iq_symbol; + + struct { + /* + * Bit 0: 0=VTM/1=SCAN + * Bit 1: Set Gain + */ + u8 flags; + u8 roll_off; + u8 rsvd1; + u8 rsvd2; + u32 frequency; + u32 symbol_rate; /* Only in VTM mode */ + u16 gain; + } sx8_start_iq; + + struct { + /* + * Bit 1:0 = STVVGLNA Gain. + * 0 = AGC, 1 = 0dB, 2 = Minimum, 3 = Maximum + */ + u8 flags; + } sx8_input_enable; }; }; @@ -116,34 +159,49 @@ struct mci_result { union { u32 status_word; struct { - u8 status; - u8 rsvd; + u8 status; + u8 mode; u16 time; }; }; union { u32 result[27]; struct { + /* 1 = DVB-S, 2 = DVB-S2X */ u8 standard; /* puncture rate for DVB-S */ u8 pls_code; - /* 7-6: rolloff, 5-2: rsrvd, 1:short, 0:pilots */ + /* 2-0: rolloff */ u8 roll_off; u8 rsvd; + /* actual frequency in Hz */ u32 frequency; + /* actual symbolrate in Hz */ u32 symbol_rate; + /* channel power in dBm x 100 */ s16 channel_power; + /* band power in dBm x 100 */ s16 band_power; + /* + * SNR in dB x 100 + * Note: negative values are valid in DVB-S2 + */ s16 signal_to_noise; s16 rsvd2; + /* + * Counter for packet errors + * (set to 0 on start command) + */ u32 packet_errors; + /* Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X */ u32 ber_numerator; u32 ber_denominator; } dvbs2_signal_info; + struct { s16 i; s16 q; - } dvbs2_signal_iq; + } iq_symbol; }; u32 version[4]; }; -- cgit v1.2.3-70-g09d2 From 07b12de21258e1d96445572100961d1d42b1b7f7 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:09 -0400 Subject: media: ddbridge/mci: store mci type and number of ports in the hwinfo For better support for future MCI based cards, rename the mci struct member to mci_ports to carry the number of ports on the cards, and add a mci_type member to identify the card type to handle differing hardware. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-core.c | 4 ++-- drivers/media/pci/ddbridge/ddbridge-hw.c | 3 ++- drivers/media/pci/ddbridge/ddbridge.h | 9 ++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 68ea0ffdad2d..67b60da12cf4 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1893,12 +1893,12 @@ static void ddb_port_probe(struct ddb_port *port) } if (link->info->type == DDB_OCTOPUS_MCI) { - if (port->nr >= link->info->mci) + if (port->nr >= link->info->mci_ports) return; port->name = "DUAL MCI"; port->type_name = "MCI"; port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_MCI_SX8; + port->type = DDB_TUNER_MCI + link->info->mci_type; return; } diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.c b/drivers/media/pci/ddbridge/ddbridge-hw.c index 1d3ee6accdd5..f3cbac07b41f 100644 --- a/drivers/media/pci/ddbridge/ddbridge-hw.c +++ b/drivers/media/pci/ddbridge/ddbridge-hw.c @@ -318,7 +318,8 @@ static const struct ddb_info ddb_s2x_48 = { .port_num = 4, .i2c_mask = 0x00, .tempmon_irq = 24, - .mci = 4 + .mci_ports = 4, + .mci_type = 0, }; /****************************************************************************/ diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 65bd74d86ed5..8a354dfb6c22 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -120,20 +120,23 @@ struct ddb_info { #define DDB_OCTOPUS_MCI 9 char *name; u32 i2c_mask; + u32 board_control; + u32 board_control_2; + u8 port_num; u8 led_num; u8 fan_num; u8 temp_num; u8 temp_bus; - u32 board_control; - u32 board_control_2; u8 con_clock; /* use a continuous clock */ u8 ts_quirks; #define TS_QUIRK_SERIAL 1 #define TS_QUIRK_REVERSED 2 #define TS_QUIRK_ALT_OSC 8 + u8 mci_ports; + u8 mci_type; + u32 tempmon_irq; - u8 mci; const struct ddb_regmap *regmap; }; -- cgit v1.2.3-70-g09d2 From 84409a95bbae75c5396c6db9ac43f1d1626d6b75 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:10 -0400 Subject: media: ddbridge/mci: make ddb_mci_cmd() and ddb_mci_config() public In preparation for splitting all MaxSX8 related code parts from the common MCI code, prefix both mci_cmd() and mci_config() functions with ddb_, remove the static marking and add matching function prototypes to ddbridge-mci.h so these functions can be reused from other files within the ddbridge driver. As this requires the mci-related structs to be defined in ddbridge-mci.h, move struct mci and struct mci_base there and clean them up. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.c | 62 ++++++++----------------------- drivers/media/pci/ddbridge/ddbridge-mci.h | 36 ++++++++++++++++++ 2 files changed, 51 insertions(+), 47 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index fa0d7d0cc6f6..a29ff25d9029 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -26,38 +26,6 @@ static const u32 MCLK = (1550000000 / 12); static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); static const u32 MAX_LDPC_BITRATE = (720000000); -struct mci_base { - struct list_head mci_list; - void *key; - struct ddb_link *link; - struct completion completion; - - struct device *dev; - struct mutex tuner_lock; /* concurrent tuner access lock */ - u8 adr; - struct mutex mci_lock; /* concurrent MCI access lock */ - int count; - - u8 tuner_use_count[MCI_TUNER_MAX]; - u8 assigned_demod[MCI_DEMOD_MAX]; - u32 used_ldpc_bitrate[MCI_DEMOD_MAX]; - u8 demod_in_use[MCI_DEMOD_MAX]; - u32 iq_mode; -}; - -struct mci { - struct mci_base *base; - struct dvb_frontend fe; - int nr; - int demod; - int tuner; - int first_time_lock; - int started; - struct mci_result signal_info; - - u32 bb_mode; -}; - static int mci_reset(struct mci *state) { struct ddb_link *link = state->base->link; @@ -84,7 +52,7 @@ static int mci_reset(struct mci *state) return 0; } -static int mci_config(struct mci *state, u32 config) +int ddb_mci_config(struct mci *state, u32 config) { struct ddb_link *link = state->base->link; @@ -122,9 +90,9 @@ static int _mci_cmd_unlocked(struct mci *state, return 0; } -static int mci_cmd(struct mci *state, - struct mci_command *command, - struct mci_result *result) +int ddb_mci_cmd(struct mci *state, + struct mci_command *command, + struct mci_result *result) { int stat; @@ -164,7 +132,7 @@ static int get_info(struct dvb_frontend *fe) memset(&cmd, 0, sizeof(cmd)); cmd.command = MCI_CMD_GETSIGNALINFO; cmd.demod = state->demod; - stat = mci_cmd(state, &cmd, &state->signal_info); + stat = ddb_mci_cmd(state, &cmd, &state->signal_info); return stat; } @@ -205,7 +173,7 @@ static int read_status(struct dvb_frontend *fe, enum fe_status *status) cmd.command = MCI_CMD_GETSTATUS; cmd.demod = state->demod; - stat = mci_cmd(state, &cmd, &res); + stat = ddb_mci_cmd(state, &cmd, &res); if (stat) return stat; *status = 0x00; @@ -228,7 +196,7 @@ static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) memset(&cmd, 0, sizeof(cmd)); cmd.tuner = state->tuner; cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; - return mci_cmd(state, &cmd, NULL); + return ddb_mci_cmd(state, &cmd, NULL); } static int stop(struct dvb_frontend *fe) @@ -241,13 +209,13 @@ static int stop(struct dvb_frontend *fe) if (state->demod != DEMOD_UNUSED) { cmd.command = MCI_CMD_STOP; cmd.demod = state->demod; - mci_cmd(state, &cmd, NULL); + ddb_mci_cmd(state, &cmd, NULL); if (state->base->iq_mode) { cmd.command = MCI_CMD_STOP; cmd.demod = state->demod; cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, SX8_TSCONFIG_MODE_NORMAL); + ddb_mci_cmd(state, &cmd, NULL); + ddb_mci_config(state, SX8_TSCONFIG_MODE_NORMAL); } } mutex_lock(&state->base->tuner_lock); @@ -345,8 +313,8 @@ unlock: cmd.command = SX8_CMD_ENABLE_IQOUTPUT; cmd.demod = state->demod; cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, ts_config); + ddb_mci_cmd(state, &cmd, NULL); + ddb_mci_config(state, ts_config); } if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) flags |= 0x80; @@ -368,7 +336,7 @@ unlock: cmd.output = state->nr; if (p->stream_id == 0x80000000) cmd.output |= 0x80; - stat = mci_cmd(state, &cmd, NULL); + stat = ddb_mci_cmd(state, &cmd, NULL); if (stat) stop(fe); return stat; @@ -413,8 +381,8 @@ unlock: cmd.tuner = state->tuner; cmd.demod = state->demod; cmd.output = 7; - mci_config(state, ts_config); - stat = mci_cmd(state, &cmd, NULL); + ddb_mci_config(state, ts_config); + stat = ddb_mci_cmd(state, &cmd, NULL); if (stat) stop(fe); return stat; diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 5e0c9e88b6fc..14c8b1ee6358 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -206,6 +206,42 @@ struct mci_result { u32 version[4]; }; +struct mci_base { + struct list_head mci_list; + void *key; + struct ddb_link *link; + struct completion completion; + + struct device *dev; + struct mutex tuner_lock; /* concurrent tuner access lock */ + u8 adr; + struct mutex mci_lock; /* concurrent MCI access lock */ + int count; + + u8 tuner_use_count[MCI_TUNER_MAX]; + u8 assigned_demod[MCI_DEMOD_MAX]; + u32 used_ldpc_bitrate[MCI_DEMOD_MAX]; + u8 demod_in_use[MCI_DEMOD_MAX]; + u32 iq_mode; +}; + +struct mci { + struct mci_base *base; + struct dvb_frontend fe; + int nr; + int demod; + int tuner; + int first_time_lock; + int started; + struct mci_result signal_info; + + u32 bb_mode; +}; + +int ddb_mci_cmd(struct mci *state, struct mci_command *command, + struct mci_result *result); +int ddb_mci_config(struct mci *state, u32 config); + struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int nr, -- cgit v1.2.3-70-g09d2 From e552684809d601c9a6109a73322c816ce1de10bd Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:11 -0400 Subject: media: ddbridge/mci: split MaxSX8 specific code off to ddbridge-sx8.c Split off all code specific to the MaxSX8 cards to a separate ddbridge-sx8 module and hook it up in the Makefile. This also adds evaluation of the mci_type to allow for using different attach handling for different cards. As different cards can implement things differently (ie. support differing frontend_ops, and have different base structs being put ontop of the common mci_base struct), this introduces the mci_cfg struct which is initially used to hold a few specifics to the -sx8 submodule. While at it, the handling of the i/q mode is adjusted slightly. Besides this and handling mci_base and sx8_base struct pointers where needed, all code is copied unmodified from ddbridge-mci.c. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/Makefile | 3 +- drivers/media/pci/ddbridge/ddbridge-core.c | 2 +- drivers/media/pci/ddbridge/ddbridge-max.c | 18 +- drivers/media/pci/ddbridge/ddbridge-max.h | 2 +- drivers/media/pci/ddbridge/ddbridge-mci.c | 408 +------------------------ drivers/media/pci/ddbridge/ddbridge-mci.h | 28 +- drivers/media/pci/ddbridge/ddbridge-sx8.c | 474 +++++++++++++++++++++++++++++ 7 files changed, 516 insertions(+), 419 deletions(-) create mode 100644 drivers/media/pci/ddbridge/ddbridge-sx8.c (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index 9b9e35f171b7..5b6d5bbc38af 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -4,7 +4,8 @@ # ddbridge-objs := ddbridge-main.o ddbridge-core.o ddbridge-ci.o \ - ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o + ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o \ + ddbridge-sx8.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 67b60da12cf4..c1b982e8e6c9 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1593,7 +1593,7 @@ static int dvb_input_attach(struct ddb_input *input) goto err_detach; break; case DDB_TUNER_MCI_SX8: - if (ddb_fe_attach_mci(input) < 0) + if (ddb_fe_attach_mci(input, port->type) < 0) goto err_detach; break; default: diff --git a/drivers/media/pci/ddbridge/ddbridge-max.c b/drivers/media/pci/ddbridge/ddbridge-max.c index 739e4b444cf4..8da1c7b91577 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.c +++ b/drivers/media/pci/ddbridge/ddbridge-max.c @@ -457,21 +457,29 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input) /******************************************************************************/ /* MAX MCI related functions */ -int ddb_fe_attach_mci(struct ddb_input *input) +int ddb_fe_attach_mci(struct ddb_input *input, u32 type) { struct ddb *dev = input->port->dev; struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; struct ddb_port *port = input->port; struct ddb_link *link = &dev->link[port->lnr]; int demod, tuner; + struct mci_cfg cfg; demod = input->nr; tuner = demod & 3; - if (fmode == 3) - tuner = 0; - dvb->fe = ddb_mci_attach(input, 0, demod, &dvb->set_input); + switch (type) { + case DDB_TUNER_MCI_SX8: + cfg = ddb_max_sx8_cfg; + if (fmode == 3) + tuner = 0; + break; + default: + return -EINVAL; + } + dvb->fe = ddb_mci_attach(input, &cfg, demod, &dvb->set_input); if (!dvb->fe) { - dev_err(dev->dev, "No MAXSX8 found!\n"); + dev_err(dev->dev, "No MCI card found!\n"); return -ENODEV; } if (!dvb->set_input) { diff --git a/drivers/media/pci/ddbridge/ddbridge-max.h b/drivers/media/pci/ddbridge/ddbridge-max.h index 82efc53baa94..9838c73973b6 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.h +++ b/drivers/media/pci/ddbridge/ddbridge-max.h @@ -25,6 +25,6 @@ int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm); int ddb_fe_attach_mxl5xx(struct ddb_input *input); -int ddb_fe_attach_mci(struct ddb_input *input); +int ddb_fe_attach_mci(struct ddb_input *input, u32 type); #endif /* _DDBRIDGE_MAX_H */ diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index a29ff25d9029..97384ae9ad27 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -22,10 +22,6 @@ static LIST_HEAD(mci_list); -static const u32 MCLK = (1550000000 / 12); -static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); -static const u32 MAX_LDPC_BITRATE = (720000000); - static int mci_reset(struct mci *state) { struct ddb_link *link = state->base->link; @@ -99,7 +95,7 @@ int ddb_mci_cmd(struct mci *state, mutex_lock(&state->base->mci_lock); stat = _mci_cmd_unlocked(state, (u32 *)command, sizeof(*command) / sizeof(u32), - (u32 *)result, sizeof(*result) / sizeof(u32)); + (u32 *)result, sizeof(*result) / sizeof(u32)); mutex_unlock(&state->base->mci_lock); return stat; } @@ -111,389 +107,6 @@ static void mci_handler(void *priv) complete(&base->completion); } -static void release(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - - state->base->count--; - if (state->base->count == 0) { - list_del(&state->base->mci_list); - kfree(state->base); - } - kfree(state); -} - -static int get_info(struct dvb_frontend *fe) -{ - int stat; - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.command = MCI_CMD_GETSIGNALINFO; - cmd.demod = state->demod; - stat = ddb_mci_cmd(state, &cmd, &state->signal_info); - return stat; -} - -static int get_snr(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - - p->cnr.len = 1; - p->cnr.stat[0].scale = FE_SCALE_DECIBEL; - p->cnr.stat[0].svalue = - (s64)state->signal_info.dvbs2_signal_info.signal_to_noise - * 10; - return 0; -} - -static int get_strength(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - s32 str; - - str = 100000 - - (state->signal_info.dvbs2_signal_info.channel_power - * 10 + 108750); - p->strength.len = 1; - p->strength.stat[0].scale = FE_SCALE_DECIBEL; - p->strength.stat[0].svalue = str; - return 0; -} - -static int read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - int stat; - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - struct mci_result res; - - cmd.command = MCI_CMD_GETSTATUS; - cmd.demod = state->demod; - stat = ddb_mci_cmd(state, &cmd, &res); - if (stat) - return stat; - *status = 0x00; - get_info(fe); - get_strength(fe); - if (res.status == SX8_DEMOD_WAIT_MATYPE) - *status = 0x0f; - if (res.status == SX8_DEMOD_LOCKED) { - *status = 0x1f; - get_snr(fe); - } - return stat; -} - -static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.tuner = state->tuner; - cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; - return ddb_mci_cmd(state, &cmd, NULL); -} - -static int stop(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - u32 input = state->tuner; - - memset(&cmd, 0, sizeof(cmd)); - if (state->demod != DEMOD_UNUSED) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - ddb_mci_cmd(state, &cmd, NULL); - if (state->base->iq_mode) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - cmd.output = 0; - ddb_mci_cmd(state, &cmd, NULL); - ddb_mci_config(state, SX8_TSCONFIG_MODE_NORMAL); - } - } - mutex_lock(&state->base->tuner_lock); - state->base->tuner_use_count[input]--; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 0); - if (state->demod < MCI_DEMOD_MAX) - state->base->demod_in_use[state->demod] = 0; - state->base->used_ldpc_bitrate[state->nr] = 0; - state->demod = DEMOD_UNUSED; - state->base->assigned_demod[state->nr] = DEMOD_UNUSED; - state->base->iq_mode = 0; - mutex_unlock(&state->base->tuner_lock); - state->started = 0; - return 0; -} - -static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - u32 bits_per_symbol = 0; - int i, stat = 0; - - if (p->symbol_rate >= (MCLK / 2)) - flags &= ~1; - if ((flags & 3) == 0) - return -EINVAL; - - if (flags & 2) { - u32 tmp = modmask; - - bits_per_symbol = 1; - while (tmp & 1) { - tmp >>= 1; - bits_per_symbol++; - } - } - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < MCI_DEMOD_MAX; i++) { - used_ldpc_bitrate += state->base->used_ldpc_bitrate[i]; - if (state->base->demod_in_use[i]) - used_demods++; - } - if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || - ((ts_config & SX8_TSCONFIG_MODE_MASK) > - SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { - stat = -EBUSY; - goto unlock; - } - free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; - if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) - free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; - - while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) - bits_per_symbol--; - - if (bits_per_symbol < 2) { - stat = -EBUSY; - goto unlock; - } - i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; - while (i >= 0 && state->base->demod_in_use[i]) - i--; - if (i < 0) { - stat = -EBUSY; - goto unlock; - } - state->base->demod_in_use[i] = 1; - state->base->used_ldpc_bitrate[state->nr] = p->symbol_rate - * bits_per_symbol; - state->demod = i; - state->base->assigned_demod[state->nr] = i; - - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - memset(&cmd, 0, sizeof(cmd)); - - if (state->base->iq_mode) { - cmd.command = SX8_CMD_ENABLE_IQOUTPUT; - cmd.demod = state->demod; - cmd.output = 0; - ddb_mci_cmd(state, &cmd, NULL); - ddb_mci_config(state, ts_config); - } - if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) - flags |= 0x80; - dev_dbg(state->base->dev, "MCI-%d: tuner=%d demod=%d\n", - state->nr, state->tuner, state->demod); - cmd.command = MCI_CMD_SEARCH_DVBS; - cmd.dvbs2_search.flags = flags; - cmd.dvbs2_search.s2_modulation_mask = - modmask & ((1 << (bits_per_symbol - 1)) - 1); - cmd.dvbs2_search.retry = 2; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.dvbs2_search.scrambling_sequence_index = - p->scrambling_sequence_index; - cmd.dvbs2_search.input_stream_id = - (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = state->nr; - if (p->stream_id == 0x80000000) - cmd.output |= 0x80; - stat = ddb_mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - -static int start_iq(struct dvb_frontend *fe, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - int i, stat = 0; - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < MCI_DEMOD_MAX; i++) - if (state->base->demod_in_use[i]) - used_demods++; - if (used_demods > 0) { - stat = -EBUSY; - goto unlock; - } - state->demod = 0; - state->base->assigned_demod[state->nr] = 0; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - - memset(&cmd, 0, sizeof(cmd)); - cmd.command = SX8_CMD_START_IQ; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = 7; - ddb_mci_config(state, ts_config); - stat = ddb_mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - -static int set_parameters(struct dvb_frontend *fe) -{ - int stat = 0; - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 ts_config, iq_mode = 0, isi; - - if (state->started) - stop(fe); - - isi = p->stream_id; - if (isi != NO_STREAM_ID_FILTER) - iq_mode = (isi & 0x30000000) >> 28; - - switch (iq_mode) { - case 1: - ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); - break; - case 2: - ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); - break; - default: - ts_config = SX8_TSCONFIG_MODE_NORMAL; - break; - } - - if (iq_mode != 2) { - u32 flags = 3; - u32 mask = 3; - - if (p->modulation == APSK_16 || - p->modulation == APSK_32) { - flags = 2; - mask = 15; - } - stat = start(fe, flags, mask, ts_config); - } else { - stat = start_iq(fe, ts_config); - } - - if (!stat) { - state->started = 1; - state->first_time_lock = 1; - state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; - } - - return stat; -} - -static int tune(struct dvb_frontend *fe, bool re_tune, - unsigned int mode_flags, - unsigned int *delay, enum fe_status *status) -{ - int r; - - if (re_tune) { - r = set_parameters(fe); - if (r) - return r; - } - r = read_status(fe, status); - if (r) - return r; - - if (*status & FE_HAS_LOCK) - return 0; - *delay = HZ / 10; - return 0; -} - -static enum dvbfe_algo get_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int set_input(struct dvb_frontend *fe, int input) -{ - struct mci *state = fe->demodulator_priv; - - state->tuner = input; - dev_dbg(state->base->dev, "MCI-%d: input=%d\n", state->nr, input); - return 0; -} - -static struct dvb_frontend_ops mci_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2 }, - .info = { - .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, - .symbol_rate_min = 100000, - .symbol_rate_max = 100000000, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_2G_MODULATION | - FE_CAN_MULTISTREAM, - }, - .get_frontend_algo = get_algo, - .tune = tune, - .release = release, - .read_status = read_status, -}; - static struct mci_base *match_base(void *key) { struct mci_base *p; @@ -511,8 +124,7 @@ static int probe(struct mci *state) } struct dvb_frontend -*ddb_mci_attach(struct ddb_input *input, - int mci_type, int nr, +*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, int (**fn_set_input)(struct dvb_frontend *fe, int input)) { struct ddb_port *port = input->port; @@ -520,9 +132,9 @@ struct dvb_frontend struct ddb_link *link = &dev->link[port->lnr]; struct mci_base *base; struct mci *state; - void *key = mci_type ? (void *)port : (void *)link; + void *key = cfg->type ? (void *)port : (void *)link; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc(cfg->state_size, GFP_KERNEL); if (!state) return NULL; @@ -531,7 +143,7 @@ struct dvb_frontend base->count++; state->base = base; } else { - base = kzalloc(sizeof(*base), GFP_KERNEL); + base = kzalloc(cfg->base_size, GFP_KERNEL); if (!base) goto fail; base->key = key; @@ -548,15 +160,17 @@ struct dvb_frontend goto fail; } list_add(&base->mci_list, &mci_list); + if (cfg->base_init) + cfg->base_init(base); } - state->fe.ops = mci_ops; + memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops)); state->fe.demodulator_priv = state; state->nr = nr; - *fn_set_input = set_input; - + *fn_set_input = cfg->set_input; state->tuner = nr; state->demod = nr; - + if (cfg->init) + cfg->init(state); return &state->fe; fail: kfree(state); diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 14c8b1ee6358..f02bc76e3c98 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -211,18 +211,11 @@ struct mci_base { void *key; struct ddb_link *link; struct completion completion; - struct device *dev; struct mutex tuner_lock; /* concurrent tuner access lock */ - u8 adr; struct mutex mci_lock; /* concurrent MCI access lock */ int count; - - u8 tuner_use_count[MCI_TUNER_MAX]; - u8 assigned_demod[MCI_DEMOD_MAX]; - u32 used_ldpc_bitrate[MCI_DEMOD_MAX]; - u8 demod_in_use[MCI_DEMOD_MAX]; - u32 iq_mode; + int type; }; struct mci { @@ -231,20 +224,27 @@ struct mci { int nr; int demod; int tuner; - int first_time_lock; - int started; - struct mci_result signal_info; +}; - u32 bb_mode; +struct mci_cfg { + int type; + struct dvb_frontend_ops *fe_ops; + u32 base_size; + u32 state_size; + int (*init)(struct mci *mci); + int (*base_init)(struct mci_base *mci_base); + int (*set_input)(struct dvb_frontend *fe, int input); }; +/* defined in ddbridge-sx8.c */ +extern const struct mci_cfg ddb_max_sx8_cfg; + int ddb_mci_cmd(struct mci *state, struct mci_command *command, struct mci_result *result); int ddb_mci_config(struct mci *state, u32 config); struct dvb_frontend -*ddb_mci_attach(struct ddb_input *input, - int mci_type, int nr, +*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, int (**fn_set_input)(struct dvb_frontend *fe, int input)); #endif /* _DDBRIDGE_MCI_H_ */ diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c new file mode 100644 index 000000000000..1a12f2105490 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ddbridge-sx8.c: Digital Devices MAX SX8 driver + * + * Copyright (C) 2018 Digital Devices GmbH + * Marcus Metzler + * Ralph Metzler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ddbridge.h" +#include "ddbridge-io.h" +#include "ddbridge-mci.h" + +static const u32 MCLK = (1550000000 / 12); +static const u32 MAX_LDPC_BITRATE = (720000000); +static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); + +#define SX8_TUNER_NUM 4 +#define SX8_DEMOD_NUM 8 +#define SX8_DEMOD_NONE 0xff + +struct sx8_base { + struct mci_base mci_base; + + u8 tuner_use_count[SX8_TUNER_NUM]; + u32 gain_mode[SX8_TUNER_NUM]; + + u32 used_ldpc_bitrate[SX8_DEMOD_NUM]; + u8 demod_in_use[SX8_DEMOD_NUM]; + u32 iq_mode; + u32 burst_size; + u32 direct_mode; +}; + +struct sx8 { + struct mci mci; + + int first_time_lock; + int started; + struct mci_result signal_info; + + u32 bb_mode; + u32 local_frequency; +}; + +static void release(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + + mci_base->count--; + if (mci_base->count == 0) { + list_del(&mci_base->mci_list); + kfree(mci_base); + } + kfree(state); +} + +static int get_info(struct dvb_frontend *fe) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = MCI_CMD_GETSIGNALINFO; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info); + return stat; +} + +static int get_snr(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = + (s64)state->signal_info.dvbs2_signal_info.signal_to_noise + * 10; + return 0; +} + +static int get_strength(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 str; + + str = 100000 - + (state->signal_info.dvbs2_signal_info.channel_power + * 10 + 108750); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = str; + return 0; +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + struct mci_result res; + + cmd.command = MCI_CMD_GETSTATUS; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, &res); + if (stat) + return stat; + *status = 0x00; + get_info(fe); + get_strength(fe); + if (res.status == SX8_DEMOD_WAIT_MATYPE) + *status = 0x0f; + if (res.status == SX8_DEMOD_LOCKED) { + *status = 0x1f; + get_snr(fe); + } + return stat; +} + +static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tuner = state->mci.tuner; + cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; + cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner]; + return ddb_mci_cmd(&state->mci, &cmd, NULL); +} + +static int stop(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct mci_command cmd; + u32 input = state->mci.tuner; + + memset(&cmd, 0, sizeof(cmd)); + if (state->mci.demod != SX8_DEMOD_NONE) { + cmd.command = MCI_CMD_STOP; + cmd.demod = state->mci.demod; + ddb_mci_cmd(&state->mci, &cmd, NULL); + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_DISABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); + } + } + mutex_lock(&mci_base->tuner_lock); + sx8_base->tuner_use_count[input]--; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 0); + if (state->mci.demod < SX8_DEMOD_NUM) { + sx8_base->demod_in_use[state->mci.demod] = 0; + state->mci.demod = SX8_DEMOD_NONE; + } + sx8_base->used_ldpc_bitrate[state->mci.nr] = 0; + sx8_base->iq_mode = 0; + mutex_unlock(&mci_base->tuner_lock); + state->started = 0; + return 0; +} + +static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + u32 bits_per_symbol = 0; + int i = -1, stat = 0; + + if (p->symbol_rate >= (MCLK / 2)) + flags &= ~1; + if ((flags & 3) == 0) + return -EINVAL; + + if (flags & 2) { + u32 tmp = modmask; + + bits_per_symbol = 1; + while (tmp & 1) { + tmp >>= 1; + bits_per_symbol++; + } + } + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + + if (sx8_base->direct_mode) { + if (p->symbol_rate >= MCLK / 2) { + if (state->mci.nr < 4) + i = state->mci.nr; + } else { + i = state->mci.nr; + } + } else { + for (i = 0; i < SX8_DEMOD_NUM; i++) { + used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i]; + if (sx8_base->demod_in_use[i]) + used_demods++; + } + if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || + ((ts_config & SX8_TSCONFIG_MODE_MASK) > + SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { + stat = -EBUSY; + goto unlock; + } + free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; + if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) + free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; + + while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) + bits_per_symbol--; + if (bits_per_symbol < 2) { + stat = -EBUSY; + goto unlock; + } + + modmask &= ((1 << (bits_per_symbol - 1)) - 1); + if (((flags & 0x02) != 0) && modmask == 0) { + stat = -EBUSY; + goto unlock; + } + + i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; + while (i >= 0 && sx8_base->demod_in_use[i]) + i--; + } + + if (i < 0) { + stat = -EBUSY; + goto unlock; + } + sx8_base->demod_in_use[i] = 1; + sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate + * bits_per_symbol; + state->mci.demod = i; + + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + memset(&cmd, 0, sizeof(cmd)); + + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_ENABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, ts_config); + } + if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) + flags |= 0x80; + dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n", + state->mci.nr, state->mci.tuner, state->mci.demod); + cmd.command = MCI_CMD_SEARCH_DVBS; + cmd.dvbs2_search.flags = flags; + cmd.dvbs2_search.s2_modulation_mask = modmask; + cmd.dvbs2_search.retry = 2; + cmd.dvbs2_search.frequency = p->frequency * 1000; + cmd.dvbs2_search.symbol_rate = p->symbol_rate; + cmd.dvbs2_search.scrambling_sequence_index = + p->scrambling_sequence_index; + cmd.dvbs2_search.input_stream_id = + (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + cmd.output = state->mci.nr; + if (p->stream_id == 0x80000000) + cmd.output |= 0x80; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + return stat; +} + +static int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off, + u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + int i, stat = 0; + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + for (i = 0; i < SX8_DEMOD_NUM; i++) + if (sx8_base->demod_in_use[i]) + used_demods++; + if (used_demods > 0) { + stat = -EBUSY; + goto unlock; + } + state->mci.demod = 0; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = SX8_CMD_START_IQ; + cmd.sx8_start_iq.flags = flags; + cmd.sx8_start_iq.roll_off = roll_off; + cmd.sx8_start_iq.frequency = p->frequency * 1000; + cmd.sx8_start_iq.symbol_rate = p->symbol_rate; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + ddb_mci_config(&state->mci, ts_config); + return stat; +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi; + + if (state->started) + stop(fe); + + isi = p->stream_id; + if (isi != NO_STREAM_ID_FILTER) + iq_mode = (isi & 0x30000000) >> 28; + + if (iq_mode) + ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + if (iq_mode < 3) { + u32 flags = 3; + u32 mask = 0x7f; + + if (p->modulation == APSK_16 || + p->modulation == APSK_32) { + flags = 2; + mask = 0x0f; + } + stat = start(fe, flags, mask, ts_config); + } else { + u32 flags = (iq_mode == 2) ? 1 : 0; + + stat = start_iq(fe, flags, 4, ts_config); + } + if (!stat) { + state->started = 1; + state->first_time_lock = 1; + state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; + } + + return stat; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + int r; + + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + } + r = read_status(fe, status); + if (r) + return r; + + if (*status & FE_HAS_LOCK) + return 0; + *delay = HZ / 10; + return 0; +} + +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int set_input(struct dvb_frontend *fe, int input) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + + if (input >= SX8_TUNER_NUM) + return -EINVAL; + + state->mci.tuner = input; + dev_dbg(mci_base->dev, "MCI-%d: input=%d\n", state->mci.nr, input); + return 0; +} + +static struct dvb_frontend_ops sx8_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, + .symbol_rate_min = 100000, + .symbol_rate_max = 100000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM, + }, + .get_frontend_algo = get_algo, + .tune = tune, + .release = release, + .read_status = read_status, +}; + +static int init(struct mci *mci) +{ + struct sx8 *state = (struct sx8 *)mci; + + state->mci.demod = SX8_DEMOD_NONE; + return 0; +} + +const struct mci_cfg ddb_max_sx8_cfg = { + .type = 0, + .fe_ops = &sx8_ops, + .base_size = sizeof(struct sx8_base), + .state_size = sizeof(struct sx8), + .init = init, + .set_input = set_input, +}; -- cgit v1.2.3-70-g09d2 From 8610326e39cc929dd6fa49161c3472e4babe6bbc Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:12 -0400 Subject: media: ddbridge/mci: add more MCI status codes, improve MCI_SUCCESS macro The MCI can report the command status more finegrained, so, add more status code defines and update the MCI_SUCCESS macro. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index f02bc76e3c98..600a8bc642c4 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -74,9 +74,13 @@ #define SX8_CMD_ENABLE_IQOUTPUT (0x44) #define SX8_CMD_DISABLE_IQOUTPUT (0x45) -#define MCI_ERROR_UNSUPPORTED (0x80) +#define MCI_STATUS_OK (0x00) +#define MCI_STATUS_UNSUPPORTED (0x80) +#define MCI_STATUS_RETRY (0xFD) +#define MCI_STATUS_NOT_READY (0xFE) +#define MCI_STATUS_ERROR (0xFF) -#define MCI_SUCCESS(status) (status < MCI_ERROR_UNSUPPORTED) +#define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) #define SX8_CMD_DIAG_READ8 (0xE0) #define SX8_CMD_DIAG_READ32 (0xE1) -- cgit v1.2.3-70-g09d2 From 3153dfe2914bec728cab9a5c9d7b2ec71d714d9c Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:13 -0400 Subject: media: ddbridge/sx8: disable automatic PLS code search The SX8 cards by default do an automatic search for the PLS code. This is not necessarily wanted as this can eventually be detected wrong, so disable this. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-sx8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c index 1a12f2105490..c87cefa10762 100644 --- a/drivers/media/pci/ddbridge/ddbridge-sx8.c +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -292,7 +292,7 @@ unlock: cmd.dvbs2_search.frequency = p->frequency * 1000; cmd.dvbs2_search.symbol_rate = p->symbol_rate; cmd.dvbs2_search.scrambling_sequence_index = - p->scrambling_sequence_index; + p->scrambling_sequence_index | 0x80000000; cmd.dvbs2_search.input_stream_id = (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; cmd.tuner = state->mci.tuner; -- cgit v1.2.3-70-g09d2 From e1f84840fd13e5f7ef4c20e3e9d021a0d2f19e11 Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:14 -0400 Subject: media: ddbridge/sx8: enable modulation selection in set_parameters() Allow for tuning to transponders with specific modulations in set_parameters(). Setting a specific modulation will also enable lower modulations. Picked up from the upstream dddvb GIT. Upstream also has support for APSK64/128/256 modulations which aren't supported yet by the DVB API, so comment them out until support for them is added. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-sx8.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c index c87cefa10762..4418604258d1 100644 --- a/drivers/media/pci/ddbridge/ddbridge-sx8.c +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -372,15 +372,31 @@ static int set_parameters(struct dvb_frontend *fe) if (iq_mode) ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); if (iq_mode < 3) { - u32 flags = 3; - u32 mask = 0x7f; - - if (p->modulation == APSK_16 || - p->modulation == APSK_32) { - flags = 2; + u32 mask; + + switch (p->modulation) { + /* uncomment whenever these modulations hit the DVB API + * case APSK_256: + * mask = 0x7f; + * break; + * case APSK_128: + * mask = 0x3f; + * break; + * case APSK_64: + * mask = 0x1f; + * break; + */ + case APSK_32: mask = 0x0f; + break; + case APSK_16: + mask = 0x07; + break; + default: + mask = 0x03; + break; } - stat = start(fe, flags, mask, ts_config); + stat = start(fe, 3, mask, ts_config); } else { u32 flags = (iq_mode == 2) ? 1 : 0; -- cgit v1.2.3-70-g09d2 From 875658af666fb171f2b61a203281fde608bab24a Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sat, 23 Jun 2018 11:36:15 -0400 Subject: media: ddbridge/mci: add SX8 I/Q mode remark and remove DIAG CMD defines Take note that the SX8 IQ mode is only available on a single tuner, and remove the MCI/SX8 DIAG CMD defines. Picked up from the upstream dddvb GIT. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ddbridge/ddbridge-mci.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 600a8bc642c4..24241111c634 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -42,6 +42,22 @@ #define SX8_TSCONFIG_MODE_NORMAL (0x00000001) #define SX8_TSCONFIG_MODE_IQ (0x00000003) +/* + * IQMode is only available on MaxSX8 on a single tuner + * + * IQ_MODE_SAMPLES + * sampling rate is 1550/24 MHz (64.583 MHz) + * channel agc is frozen, to allow stitching the FFT results together + * + * IQ_MODE_VTM + * sampling rate is the supplied symbolrate + * channel agc is active + * + * in both cases down sampling is done with a RRC Filter (currently fixed to + * alpha = 0.05) which causes some (ca 5%) aliasing at the edges from + * outside the spectrum + */ + #define SX8_TSCONFIG_TSHEADER (0x00000004) #define SX8_TSCONFIG_BURST (0x00000008) @@ -82,14 +98,6 @@ #define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) -#define SX8_CMD_DIAG_READ8 (0xE0) -#define SX8_CMD_DIAG_READ32 (0xE1) -#define SX8_CMD_DIAG_WRITE8 (0xE2) -#define SX8_CMD_DIAG_WRITE32 (0xE3) - -#define SX8_CMD_DIAG_READRF (0xE8) -#define SX8_CMD_DIAG_WRITERF (0xE9) - struct mci_command { union { u32 command_word; -- cgit v1.2.3-70-g09d2 From ca747d041adbf78d93bbea4d125c7ddc626effba Mon Sep 17 00:00:00 2001 From: Daniel Scheller Date: Sun, 24 Jun 2018 09:42:50 -0400 Subject: media: dvb-frontends/tda18271c2dd: fix handling of DVB-T parameters Add a break statement in set_params() for the SYS_DVBT(2). As reported by gcc: drivers/media/dvb-frontends/tda18271c2dd.c:1144:3: warning: this statement may fall through [-Wimplicit-fallthrough=] There is a nested switch() inside the code with sets the tuner to the right standard. Without the break, the code will always set to DVB-C mode, with can be sub-optimal for DVB-T. Signed-off-by: Daniel Scheller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda18271c2dd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index 2e1d36ae943b..fcffc7b4acf7 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -1154,6 +1154,7 @@ static int set_params(struct dvb_frontend *fe) default: return -EINVAL; } + break; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: if (bw <= 6000000) -- cgit v1.2.3-70-g09d2 From bef1fa6ea72bb885e0285d502e35cc01198bee6c Mon Sep 17 00:00:00 2001 From: Robert Schlabbach Date: Mon, 25 Jun 2018 06:33:58 -0400 Subject: media: em28xx: disable null packet filter for WinTVdualHD This patch disables the null packet filter for the Hauppauge WinTV-dualHD. There are applications which require the unfiltered transport stream (e.g. DOCSIS segment load analyzers). Tests showed that the device is capable of delivering two unfiltered EuroDOCSIS 3.0 transport streams simultaneously, i.e. over 100 Mbit/s worth of data, without any losses. Signed-off-by: Robert Schlabbach Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index afdc92998fff..98a428769baa 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -543,7 +543,7 @@ static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xdf, 0xff, 100}, /* demod 2 reset */ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, - {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5F_TS_ENABLE, 0x00, 0xff, 50}, /* disable TS filters */ {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, {-1, -1, -1, -1}, -- cgit v1.2.3-70-g09d2 From 83b15832ab91c9a4651decb6dc40075dd979d443 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 25 Jul 2018 12:38:10 -0400 Subject: media: doc-rst: Add packed Bayer raw14 pixel formats These formats are compressed 14-bit raw bayer formats with four different pixel orders. They are similar to 10-bit variants. The formats added by this patch are V4L2_PIX_FMT_SBGGR14P V4L2_PIX_FMT_SGBRG14P V4L2_PIX_FMT_SGRBG14P V4L2_PIX_FMT_SRGGB14P Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/pixfmt-rgb.rst | 1 + Documentation/media/uapi/v4l/pixfmt-srggb14p.rst | 127 +++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 4 + include/uapi/linux/videodev2.h | 5 + 4 files changed, 137 insertions(+) create mode 100644 Documentation/media/uapi/v4l/pixfmt-srggb14p.rst (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/pixfmt-rgb.rst b/Documentation/media/uapi/v4l/pixfmt-rgb.rst index cf2ef7df9616..1f9a7e3a07c9 100644 --- a/Documentation/media/uapi/v4l/pixfmt-rgb.rst +++ b/Documentation/media/uapi/v4l/pixfmt-rgb.rst @@ -19,4 +19,5 @@ RGB Formats pixfmt-srggb10-ipu3 pixfmt-srggb12 pixfmt-srggb12p + pixfmt-srggb14p pixfmt-srggb16 diff --git a/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst b/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst new file mode 100644 index 000000000000..88d20c0e4282 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst @@ -0,0 +1,127 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-SRGGB14P: +.. _v4l2-pix-fmt-sbggr14p: +.. _v4l2-pix-fmt-sgbrg14p: +.. _v4l2-pix-fmt-sgrbg14p: + +******************************************************************************************************************************* +V4L2_PIX_FMT_SRGGB14P ('pRCC'), V4L2_PIX_FMT_SGRBG14P ('pgCC'), V4L2_PIX_FMT_SGBRG14P ('pGCC'), V4L2_PIX_FMT_SBGGR14P ('pBCC'), +******************************************************************************************************************************* + +*man V4L2_PIX_FMT_SRGGB14P(2)* + +V4L2_PIX_FMT_SGRBG14P +V4L2_PIX_FMT_SGBRG14P +V4L2_PIX_FMT_SBGGR14P +14-bit packed Bayer formats + + +Description +=========== + +These four pixel formats are packed raw sRGB / Bayer formats with 14 +bits per colour. Every four consecutive samples are packed into seven +bytes. Each of the first four bytes contain the eight high order bits +of the pixels, and the three following bytes contains the six least +significants bits of each pixel, in the same order. + +Each n-pixel row contains n/2 green samples and n/2 blue or red samples, +with alternating green-red and green-blue rows. They are conventionally +described as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example +of one of these formats: + +**Byte Order.** +Each cell is one byte. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 2 1 1 1 1 1 1 1 + + + - .. row 1 + + - start + 0: + + - B\ :sub:`00high` + + - G\ :sub:`01high` + + - B\ :sub:`02high` + + - G\ :sub:`03high` + + - G\ :sub:`01low bits 1--0`\ (bits 7--6) + B\ :sub:`00low bits 5--0`\ (bits 5--0) + + - R\ :sub:`02low bits 3--0`\ (bits 7--4) + G\ :sub:`01low bits 5--2`\ (bits 3--0) + + - G\ :sub:`03low bits 5--0`\ (bits 7--2) + R\ :sub:`02low bits 5--4`\ (bits 1--0) + + - .. row 2 + + - start + 7: + + - G\ :sub:`00high` + + - R\ :sub:`01high` + + - G\ :sub:`02high` + + - R\ :sub:`03high` + + - R\ :sub:`01low bits 1--0`\ (bits 7--6) + G\ :sub:`00low bits 5--0`\ (bits 5--0) + + - G\ :sub:`02low bits 3--0`\ (bits 7--4) + R\ :sub:`01low bits 5--2`\ (bits 3--0) + + - R\ :sub:`03low bits 5--0`\ (bits 7--2) + G\ :sub:`02low bits 5--4`\ (bits 1--0) + + - .. row 3 + + - start + 14 + + - B\ :sub:`20high` + + - G\ :sub:`21high` + + - B\ :sub:`22high` + + - G\ :sub:`23high` + + - G\ :sub:`21low bits 1--0`\ (bits 7--6) + B\ :sub:`20low bits 5--0`\ (bits 5--0) + + - R\ :sub:`22low bits 3--0`\ (bits 7--4) + G\ :sub:`21low bits 5--2`\ (bits 3--0) + + - G\ :sub:`23low bits 5--0`\ (bits 7--2) + R\ :sub:`22low bits 5--4`\ (bits 1--0) + + - .. row 4 + + - start + 21 + + - G\ :sub:`30high` + + - R\ :sub:`31high` + + - G\ :sub:`32high` + + - R\ :sub:`33high` + + - R\ :sub:`31low bits 1--0`\ (bits 7--6) + G\ :sub:`30low bits 5--0`\ (bits 5--0) + + - G\ :sub:`32low bits 3--0`\ (bits 7--4) + R\ :sub:`31low bits 5--2`\ (bits 3--0) + + - R\ :sub:`33low bits 5--0`\ (bits 7--2) + G\ :sub:`32low bits 5--4`\ (bits 1--0) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 26d9702069fd..fd8d15f12307 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1259,6 +1259,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_SGBRG12P: descr = "12-bit Bayer GBGB/RGRG Packed"; break; case V4L2_PIX_FMT_SGRBG12P: descr = "12-bit Bayer GRGR/BGBG Packed"; break; case V4L2_PIX_FMT_SRGGB12P: descr = "12-bit Bayer RGRG/GBGB Packed"; break; + case V4L2_PIX_FMT_SBGGR14P: descr = "14-bit Bayer BGBG/GRGR Packed"; break; + case V4L2_PIX_FMT_SGBRG14P: descr = "14-bit Bayer GBGB/RGRG Packed"; break; + case V4L2_PIX_FMT_SGRBG14P: descr = "14-bit Bayer GRGR/BGBG Packed"; break; + case V4L2_PIX_FMT_SRGGB14P: descr = "14-bit Bayer RGRG/GBGB Packed"; break; case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR"; break; case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG"; break; case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index d8b33095abe0..2c20b6aa5335 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -609,6 +609,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG12P v4l2_fourcc('p', 'G', 'C', 'C') #define V4L2_PIX_FMT_SGRBG12P v4l2_fourcc('p', 'g', 'C', 'C') #define V4L2_PIX_FMT_SRGGB12P v4l2_fourcc('p', 'R', 'C', 'C') + /* 14bit raw bayer packed, 7 bytes for every 4 pixels */ +#define V4L2_PIX_FMT_SBGGR14P v4l2_fourcc('p', 'B', 'E', 'E') +#define V4L2_PIX_FMT_SGBRG14P v4l2_fourcc('p', 'G', 'E', 'E') +#define V4L2_PIX_FMT_SGRBG14P v4l2_fourcc('p', 'g', 'E', 'E') +#define V4L2_PIX_FMT_SRGGB14P v4l2_fourcc('p', 'R', 'E', 'E') #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ -- cgit v1.2.3-70-g09d2 From 6e15bec49f366511ec024a556505316222ef4ade Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:12 -0400 Subject: media: v4l: Add new 10-bit packed grayscale format The new format will be called V4L2_PIX_FMT_Y10P. It is similar to the V4L2_PIX_FMT_SBGGR10P family formats but V4L2_PIX_FMT_Y10P is a grayscale format. Signed-off-by: Todor Tomov Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/pixfmt-y10p.rst | 33 ++++++++++++++++++++++++++++ Documentation/media/uapi/v4l/yuv-formats.rst | 1 + drivers/media/v4l2-core/v4l2-ioctl.c | 1 + include/uapi/linux/videodev2.h | 1 + 4 files changed, 36 insertions(+) create mode 100644 Documentation/media/uapi/v4l/pixfmt-y10p.rst (limited to 'drivers/media') diff --git a/Documentation/media/uapi/v4l/pixfmt-y10p.rst b/Documentation/media/uapi/v4l/pixfmt-y10p.rst new file mode 100644 index 000000000000..13b571306915 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-y10p.rst @@ -0,0 +1,33 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-Y10P: + +****************************** +V4L2_PIX_FMT_Y10P ('Y10P') +****************************** + +Grey-scale image as a MIPI RAW10 packed array + + +Description +=========== + +This is a packed grey-scale image format with a depth of 10 bits per +pixel. Every four consecutive pixels are packed into 5 bytes. Each of +the first 4 bytes contain the 8 high order bits of the pixels, and +the 5th byte contains the 2 least significants bits of each pixel, +in the same order. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 8 8 8 8 64 + + * - Y'\ :sub:`00[9:2]` + - Y'\ :sub:`01[9:2]` + - Y'\ :sub:`02[9:2]` + - Y'\ :sub:`03[9:2]` + - Y'\ :sub:`03[1:0]`\ (bits 7--6) Y'\ :sub:`02[1:0]`\ (bits 5--4) + Y'\ :sub:`01[1:0]`\ (bits 3--2) Y'\ :sub:`00[1:0]`\ (bits 1--0) diff --git a/Documentation/media/uapi/v4l/yuv-formats.rst b/Documentation/media/uapi/v4l/yuv-formats.rst index 3334ea445657..9ab0592d08da 100644 --- a/Documentation/media/uapi/v4l/yuv-formats.rst +++ b/Documentation/media/uapi/v4l/yuv-formats.rst @@ -29,6 +29,7 @@ to brightness information. pixfmt-y10 pixfmt-y12 pixfmt-y10b + pixfmt-y10p pixfmt-y16 pixfmt-y16-be pixfmt-y8i diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index fd8d15f12307..54afc9c7ee6e 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1184,6 +1184,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_Y16: descr = "16-bit Greyscale"; break; case V4L2_PIX_FMT_Y16_BE: descr = "16-bit Greyscale BE"; break; case V4L2_PIX_FMT_Y10BPACK: descr = "10-bit Greyscale (Packed)"; break; + case V4L2_PIX_FMT_Y10P: descr = "10-bit Greyscale (MIPI Packed)"; break; case V4L2_PIX_FMT_Y8I: descr = "Interleaved 8-bit Greyscale"; break; case V4L2_PIX_FMT_Y12I: descr = "Interleaved 12-bit Greyscale"; break; case V4L2_PIX_FMT_Z16: descr = "16-bit Depth"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 2c20b6aa5335..5d1a3685bea9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -522,6 +522,7 @@ struct v4l2_pix_format { /* Grey bit-packed formats */ #define V4L2_PIX_FMT_Y10BPACK v4l2_fourcc('Y', '1', '0', 'B') /* 10 Greyscale bit-packed */ +#define V4L2_PIX_FMT_Y10P v4l2_fourcc('Y', '1', '0', 'P') /* 10 Greyscale, MIPI RAW10 packed */ /* Palette formats */ #define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ -- cgit v1.2.3-70-g09d2 From ec6859b23f2212275fbd476df14d7a94839c2a35 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:13 -0400 Subject: media: Rename CAMSS driver path Support for camera subsystem on QComm MSM8996/APQ8096 is to be added so remove hardware version from CAMSS driver's path. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 2 +- drivers/media/platform/qcom/camss-8x16/Makefile | 11 - .../media/platform/qcom/camss-8x16/camss-csid.c | 1094 ------- .../media/platform/qcom/camss-8x16/camss-csid.h | 82 - .../media/platform/qcom/camss-8x16/camss-csiphy.c | 893 ------ .../media/platform/qcom/camss-8x16/camss-csiphy.h | 77 - .../media/platform/qcom/camss-8x16/camss-ispif.c | 1178 -------- .../media/platform/qcom/camss-8x16/camss-ispif.h | 85 - drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 3093 -------------------- drivers/media/platform/qcom/camss-8x16/camss-vfe.h | 123 - .../media/platform/qcom/camss-8x16/camss-video.c | 859 ------ .../media/platform/qcom/camss-8x16/camss-video.h | 70 - drivers/media/platform/qcom/camss-8x16/camss.c | 751 ----- drivers/media/platform/qcom/camss-8x16/camss.h | 106 - drivers/media/platform/qcom/camss/Makefile | 11 + drivers/media/platform/qcom/camss/camss-csid.c | 1094 +++++++ drivers/media/platform/qcom/camss/camss-csid.h | 82 + drivers/media/platform/qcom/camss/camss-csiphy.c | 893 ++++++ drivers/media/platform/qcom/camss/camss-csiphy.h | 77 + drivers/media/platform/qcom/camss/camss-ispif.c | 1178 ++++++++ drivers/media/platform/qcom/camss/camss-ispif.h | 85 + drivers/media/platform/qcom/camss/camss-vfe.c | 3093 ++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-vfe.h | 123 + drivers/media/platform/qcom/camss/camss-video.c | 859 ++++++ drivers/media/platform/qcom/camss/camss-video.h | 70 + drivers/media/platform/qcom/camss/camss.c | 751 +++++ drivers/media/platform/qcom/camss/camss.h | 106 + 29 files changed, 8425 insertions(+), 8425 deletions(-) delete mode 100644 drivers/media/platform/qcom/camss-8x16/Makefile delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.h delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.h delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.h delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.h delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss.c delete mode 100644 drivers/media/platform/qcom/camss-8x16/camss.h create mode 100644 drivers/media/platform/qcom/camss/Makefile create mode 100644 drivers/media/platform/qcom/camss/camss-csid.c create mode 100644 drivers/media/platform/qcom/camss/camss-csid.h create mode 100644 drivers/media/platform/qcom/camss/camss-csiphy.c create mode 100644 drivers/media/platform/qcom/camss/camss-csiphy.h create mode 100644 drivers/media/platform/qcom/camss/camss-ispif.c create mode 100644 drivers/media/platform/qcom/camss/camss-ispif.h create mode 100644 drivers/media/platform/qcom/camss/camss-vfe.c create mode 100644 drivers/media/platform/qcom/camss/camss-vfe.h create mode 100644 drivers/media/platform/qcom/camss/camss-video.c create mode 100644 drivers/media/platform/qcom/camss/camss-video.h create mode 100644 drivers/media/platform/qcom/camss/camss.c create mode 100644 drivers/media/platform/qcom/camss/camss.h (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index 3908908b1ec8..da68e6da9981 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11870,7 +11870,7 @@ L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/qcom,camss.txt F: Documentation/media/v4l-drivers/qcom_camss.rst -F: drivers/media/platform/qcom/camss-8x16/ +F: drivers/media/platform/qcom/camss/ QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 40bd3d96ca82..b25c8d3c1c31 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -90,7 +90,7 @@ config VIDEO_PXA27x This is a v4l2 driver for the PXA27x Quick Capture Interface config VIDEO_QCOM_CAMSS - tristate "Qualcomm 8x16 V4L2 Camera Subsystem driver" + tristate "Qualcomm V4L2 Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 8e5f88eb7559..08640ba87fc2 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -89,7 +89,7 @@ obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/ obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/ -obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ +obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ diff --git a/drivers/media/platform/qcom/camss-8x16/Makefile b/drivers/media/platform/qcom/camss-8x16/Makefile deleted file mode 100644 index 3c4024fbb768..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Makefile for Qualcomm CAMSS driver - -qcom-camss-objs += \ - camss.o \ - camss-csid.o \ - camss-csiphy.o \ - camss-ispif.o \ - camss-vfe.o \ - camss-video.o \ - -obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c deleted file mode 100644 index 226f36ef7419..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* - * camss-csid.c - * - * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module - * - * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "camss-csid.h" -#include "camss.h" - -#define MSM_CSID_NAME "msm_csid" - -#define CAMSS_CSID_HW_VERSION 0x0 -#define CAMSS_CSID_CORE_CTRL_0 0x004 -#define CAMSS_CSID_CORE_CTRL_1 0x008 -#define CAMSS_CSID_RST_CMD 0x00c -#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n)) -#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n)) -#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060 -#define CAMSS_CSID_IRQ_MASK 0x064 -#define CAMSS_CSID_IRQ_STATUS 0x068 -#define CAMSS_CSID_TG_CTRL 0x0a0 -#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 -#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 -#define CAMSS_CSID_TG_VC_CFG 0x0a4 -#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff -#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f -#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) - -#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 -#define DATA_TYPE_YUV422_8BIT 0x1e -#define DATA_TYPE_RAW_6BIT 0x28 -#define DATA_TYPE_RAW_8BIT 0x2a -#define DATA_TYPE_RAW_10BIT 0x2b -#define DATA_TYPE_RAW_12BIT 0x2c - -#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 -#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 -#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 -#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 - -#define CSID_RESET_TIMEOUT_MS 500 - -struct csid_fmts { - u32 code; - u8 data_type; - u8 decode_format; - u8 bpp; - u8 spp; /* bus samples per pixel */ -}; - -static const struct csid_fmts csid_input_fmts[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - } -}; - -static const struct csid_fmts *csid_get_fmt_entry(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (code == csid_input_fmts[i].code) - return &csid_input_fmts[i]; - - WARN(1, "Unknown format\n"); - - return &csid_input_fmts[0]; -} - -/* - * csid_isr - CSID module interrupt handler - * @irq: Interrupt line - * @dev: CSID device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t csid_isr(int irq, void *dev) -{ - struct csid_device *csid = dev; - u32 value; - - value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); - writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); - - if ((value >> 11) & 0x1) - complete(&csid->reset_complete); - - return IRQ_HANDLED; -} - -/* - * csid_set_clock_rates - Calculate and set clock rates on CSID module - * @csiphy: CSID device - */ -static int csid_set_clock_rates(struct csid_device *csid) -{ - struct device *dev = to_device_index(csid, csid->id); - u32 pixel_clock; - int i, j; - int ret; - - ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock); - if (ret) - pixel_clock = 0; - - for (i = 0; i < csid->nclocks; i++) { - struct camss_clock *clock = &csid->clock[i]; - - if (!strcmp(clock->name, "csi0") || - !strcmp(clock->name, "csi1")) { - u8 bpp = csid_get_fmt_entry( - csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; - u8 num_lanes = csid->phy.lane_cnt; - u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); - long rate; - - camss_add_clock_margin(&min_rate); - - for (j = 0; j < clock->nfreqs; j++) - if (min_rate < clock->freq[j]) - break; - - if (j == clock->nfreqs) { - dev_err(dev, - "Pixel clock is too high for CSID\n"); - return -EINVAL; - } - - /* if sensor pixel clock is not available */ - /* set highest possible CSID clock rate */ - if (min_rate == 0) - j = clock->nfreqs - 1; - - rate = clk_round_rate(clock->clk, clock->freq[j]); - if (rate < 0) { - dev_err(dev, "clk round rate failed: %ld\n", - rate); - return -EINVAL; - } - - ret = clk_set_rate(clock->clk, rate); - if (ret < 0) { - dev_err(dev, "clk set rate failed: %d\n", ret); - return ret; - } - } - } - - return 0; -} - -/* - * csid_reset - Trigger reset on CSID module and wait to complete - * @csid: CSID device - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_reset(struct csid_device *csid) -{ - unsigned long time; - - reinit_completion(&csid->reset_complete); - - writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); - - time = wait_for_completion_timeout(&csid->reset_complete, - msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); - if (!time) { - dev_err(to_device_index(csid, csid->id), - "CSID reset timeout\n"); - return -EIO; - } - - return 0; -} - -/* - * csid_set_power - Power on/off CSID module - * @sd: CSID V4L2 subdevice - * @on: Requested power state - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_set_power(struct v4l2_subdev *sd, int on) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csid, csid->id); - int ret; - - if (on) { - u32 hw_version; - - ret = regulator_enable(csid->vdda); - if (ret < 0) - return ret; - - ret = csid_set_clock_rates(csid); - if (ret < 0) { - regulator_disable(csid->vdda); - return ret; - } - - ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); - if (ret < 0) { - regulator_disable(csid->vdda); - return ret; - } - - enable_irq(csid->irq); - - ret = csid_reset(csid); - if (ret < 0) { - disable_irq(csid->irq); - camss_disable_clocks(csid->nclocks, csid->clock); - regulator_disable(csid->vdda); - return ret; - } - - hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); - dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version); - } else { - disable_irq(csid->irq); - camss_disable_clocks(csid->nclocks, csid->clock); - ret = regulator_disable(csid->vdda); - } - - return ret; -} - -/* - * csid_set_stream - Enable/disable streaming on CSID module - * @sd: CSID V4L2 subdevice - * @enable: Requested streaming state - * - * Main configuration of CSID module is also done here. - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct csid_testgen_config *tg = &csid->testgen; - u32 val; - - if (enable) { - u8 vc = 0; /* Virtual Channel 0 */ - u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ - u8 dt, dt_shift, df; - int ret; - - ret = v4l2_ctrl_handler_setup(&csid->ctrls); - if (ret < 0) { - dev_err(to_device_index(csid, csid->id), - "could not sync v4l2 controls: %d\n", ret); - return ret; - } - - if (!tg->enabled && - !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) - return -ENOLINK; - - dt = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SRC].code)-> - data_type; - - if (tg->enabled) { - /* Config Test Generator */ - struct v4l2_mbus_framefmt *f = - &csid->fmt[MSM_CSID_PAD_SRC]; - u8 bpp = csid_get_fmt_entry(f->code)->bpp; - u8 spp = csid_get_fmt_entry(f->code)->spp; - u32 num_bytes_per_line = f->width * bpp * spp / 8; - u32 num_lines = f->height; - - /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ - /* 1:0 VC */ - val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | - ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); - writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); - - /* 28:16 bytes per lines, 12:0 num of lines */ - val = ((num_bytes_per_line & 0x1fff) << 16) | - (num_lines & 0x1fff); - writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_0(0)); - - /* 5:0 data type */ - val = dt; - writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_1(0)); - - /* 2:0 output test pattern */ - val = tg->payload_mode; - writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_2(0)); - } else { - struct csid_phy_config *phy = &csid->phy; - - val = phy->lane_cnt - 1; - val |= phy->lane_assign << 4; - - writel_relaxed(val, - csid->base + CAMSS_CSID_CORE_CTRL_0); - - val = phy->csiphy_id << 17; - val |= 0x9; - - writel_relaxed(val, - csid->base + CAMSS_CSID_CORE_CTRL_1); - } - - /* Config LUT */ - - dt_shift = (cid % 4) * 8; - df = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SINK].code)-> - decode_format; - - val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); - val &= ~(0xff << dt_shift); - val |= dt << dt_shift; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); - - val = (df << 4) | 0x3; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); - - if (tg->enabled) { - val = CAMSS_CSID_TG_CTRL_ENABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); - } - } else { - if (tg->enabled) { - val = CAMSS_CSID_TG_CTRL_DISABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); - } - } - - return 0; -} - -/* - * __csid_get_format - Get pointer to format structure - * @csid: CSID device - * @cfg: V4L2 subdev pad configuration - * @pad: pad from which format is requested - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE format structure - */ -static struct v4l2_mbus_framefmt * -__csid_get_format(struct csid_device *csid, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad); - - return &csid->fmt[pad]; -} - -/* - * csid_try_format - Handle try format by pad subdev method - * @csid: CSID device - * @cfg: V4L2 subdev pad configuration - * @pad: pad on which format is requested - * @fmt: pointer to v4l2 format structure - * @which: wanted subdev format - */ -static void csid_try_format(struct csid_device *csid, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) -{ - unsigned int i; - - switch (pad) { - case MSM_CSID_PAD_SINK: - /* Set format on sink pad */ - - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (fmt->code == csid_input_fmts[i].code) - break; - - /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - - fmt->width = clamp_t(u32, fmt->width, 1, 8191); - fmt->height = clamp_t(u32, fmt->height, 1, 8191); - - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - break; - - case MSM_CSID_PAD_SRC: - if (csid->testgen_mode->cur.val == 0) { - /* Test generator is disabled, keep pad formats */ - /* in sync - set and return a format same as sink pad */ - struct v4l2_mbus_framefmt format; - - format = *__csid_get_format(csid, cfg, - MSM_CSID_PAD_SINK, which); - *fmt = format; - } else { - /* Test generator is enabled, set format on source*/ - /* pad to allow test generator usage */ - - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (csid_input_fmts[i].code == fmt->code) - break; - - /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - - fmt->width = clamp_t(u32, fmt->width, 1, 8191); - fmt->height = clamp_t(u32, fmt->height, 1, 8191); - - fmt->field = V4L2_FIELD_NONE; - } - break; - } - - fmt->colorspace = V4L2_COLORSPACE_SRGB; -} - -/* - * csid_enum_mbus_code - Handle pixel format enumeration - * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * return -EINVAL or zero on success - */ -static int csid_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) - return -EINVAL; - - code->code = csid_input_fmts[code->index].code; - } else { - if (csid->testgen_mode->cur.val == 0) { - if (code->index > 0) - return -EINVAL; - - format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, - code->which); - - code->code = format->code; - } else { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) - return -EINVAL; - - code->code = csid_input_fmts[code->index].code; - } - } - - return 0; -} - -/* - * csid_enum_frame_size - Handle frame size enumeration - * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fse: pointer to v4l2_subdev_frame_size_enum structure - * return -EINVAL or zero on success - */ -static int csid_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt format; - - if (fse->index != 0) - return -EINVAL; - - format.code = fse->code; - format.width = 1; - format.height = 1; - csid_try_format(csid, cfg, fse->pad, &format, fse->which); - fse->min_width = format.width; - fse->min_height = format.height; - - if (format.code != fse->code) - return -EINVAL; - - format.code = fse->code; - format.width = -1; - format.height = -1; - csid_try_format(csid, cfg, fse->pad, &format, fse->which); - fse->max_width = format.width; - fse->max_height = format.height; - - return 0; -} - -/* - * csid_get_format - Handle get format by pads subdev method - * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int csid_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - fmt->format = *format; - - return 0; -} - -/* - * csid_set_format - Handle set format by pads subdev method - * @sd: CSID V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int csid_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which); - *format = fmt->format; - - /* Propagate the format from sink to source */ - if (fmt->pad == MSM_CSID_PAD_SINK) { - format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC, - fmt->which); - - *format = fmt->format; - csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format, - fmt->which); - } - - return 0; -} - -/* - * csid_init_formats - Initialize formats on all pads - * @sd: CSID V4L2 subdevice - * @fh: V4L2 subdev file handle - * - * Initialize all pad formats with default values. - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_subdev_format format = { - .pad = MSM_CSID_PAD_SINK, - .which = fh ? V4L2_SUBDEV_FORMAT_TRY : - V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .width = 1920, - .height = 1080 - } - }; - - return csid_set_format(sd, fh ? fh->pad : NULL, &format); -} - -static const char * const csid_test_pattern_menu[] = { - "Disabled", - "Incrementing", - "Alternating 0x55/0xAA", - "All Zeros 0x00", - "All Ones 0xFF", - "Pseudo-random Data", -}; - -/* - * csid_set_test_pattern - Set test generator's pattern mode - * @csid: CSID device - * @value: desired test pattern mode - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_set_test_pattern(struct csid_device *csid, s32 value) -{ - struct csid_testgen_config *tg = &csid->testgen; - - /* If CSID is linked to CSIPHY, do not allow to enable test generator */ - if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) - return -EBUSY; - - tg->enabled = !!value; - - switch (value) { - case 1: - tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING; - break; - case 2: - tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA; - break; - case 3: - tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES; - break; - case 4: - tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES; - break; - case 5: - tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM; - break; - } - - return 0; -} - -/* - * csid_s_ctrl - Handle set control subdev method - * @ctrl: pointer to v4l2 control structure - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct csid_device *csid = container_of(ctrl->handler, - struct csid_device, ctrls); - int ret = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_TEST_PATTERN: - ret = csid_set_test_pattern(csid, ctrl->val); - break; - } - - return ret; -} - -static const struct v4l2_ctrl_ops csid_ctrl_ops = { - .s_ctrl = csid_s_ctrl, -}; - -/* - * msm_csid_subdev_init - Initialize CSID device structure and resources - * @csid: CSID device - * @res: CSID module resources table - * @id: CSID module id - * - * Return 0 on success or a negative error code otherwise - */ -int msm_csid_subdev_init(struct csid_device *csid, - const struct resources *res, u8 id) -{ - struct device *dev = to_device_index(csid, id); - struct platform_device *pdev = to_platform_device(dev); - struct resource *r; - int i, j; - int ret; - - csid->id = id; - - /* Memory */ - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); - csid->base = devm_ioremap_resource(dev, r); - if (IS_ERR(csid->base)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(csid->base); - } - - /* Interrupt */ - - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } - - csid->irq = r->start; - snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", - dev_name(dev), MSM_CSID_NAME, csid->id); - ret = devm_request_irq(dev, csid->irq, csid_isr, - IRQF_TRIGGER_RISING, csid->irq_name, csid); - if (ret < 0) { - dev_err(dev, "request_irq failed: %d\n", ret); - return ret; - } - - disable_irq(csid->irq); - - /* Clocks */ - - csid->nclocks = 0; - while (res->clock[csid->nclocks]) - csid->nclocks++; - - csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), - GFP_KERNEL); - if (!csid->clock) - return -ENOMEM; - - for (i = 0; i < csid->nclocks; i++) { - struct camss_clock *clock = &csid->clock[i]; - - clock->clk = devm_clk_get(dev, res->clock[i]); - if (IS_ERR(clock->clk)) - return PTR_ERR(clock->clk); - - clock->name = res->clock[i]; - - clock->nfreqs = 0; - while (res->clock_rate[i][clock->nfreqs]) - clock->nfreqs++; - - if (!clock->nfreqs) { - clock->freq = NULL; - continue; - } - - clock->freq = devm_kcalloc(dev, - clock->nfreqs, - sizeof(*clock->freq), - GFP_KERNEL); - if (!clock->freq) - return -ENOMEM; - - for (j = 0; j < clock->nfreqs; j++) - clock->freq[j] = res->clock_rate[i][j]; - } - - /* Regulator */ - - csid->vdda = devm_regulator_get(dev, res->regulator[0]); - if (IS_ERR(csid->vdda)) { - dev_err(dev, "could not get regulator\n"); - return PTR_ERR(csid->vdda); - } - - init_completion(&csid->reset_complete); - - return 0; -} - -/* - * msm_csid_get_csid_id - Get CSID HW module id - * @entity: Pointer to CSID media entity structure - * @id: Return CSID HW module id here - */ -void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); - struct csid_device *csid = v4l2_get_subdevdata(sd); - - *id = csid->id; -} - -/* - * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter - * @lane_cfg - CSI2 lane configuration - * - * Return lane assign - */ -static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) -{ - u32 lane_assign = 0; - int i; - - for (i = 0; i < lane_cfg->num_data; i++) - lane_assign |= lane_cfg->data[i].pos << (i * 4); - - return lane_assign; -} - -/* - * csid_link_setup - Setup CSID connections - * @entity: Pointer to media entity structure - * @local: Pointer to local pad - * @remote: Pointer to remote pad - * @flags: Link flags - * - * Return 0 on success - */ -static int csid_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) -{ - if (flags & MEDIA_LNK_FL_ENABLED) - if (media_entity_remote_pad(local)) - return -EBUSY; - - if ((local->flags & MEDIA_PAD_FL_SINK) && - (flags & MEDIA_LNK_FL_ENABLED)) { - struct v4l2_subdev *sd; - struct csid_device *csid; - struct csiphy_device *csiphy; - struct csiphy_lanes_cfg *lane_cfg; - struct v4l2_subdev_format format = { 0 }; - - sd = media_entity_to_v4l2_subdev(entity); - csid = v4l2_get_subdevdata(sd); - - /* If test generator is enabled */ - /* do not allow a link from CSIPHY to CSID */ - if (csid->testgen_mode->cur.val != 0) - return -EBUSY; - - sd = media_entity_to_v4l2_subdev(remote->entity); - csiphy = v4l2_get_subdevdata(sd); - - /* If a sensor is not linked to CSIPHY */ - /* do no allow a link from CSIPHY to CSID */ - if (!csiphy->cfg.csi2) - return -EPERM; - - csid->phy.csiphy_id = csiphy->id; - - lane_cfg = &csiphy->cfg.csi2->lane_cfg; - csid->phy.lane_cnt = lane_cfg->num_data; - csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); - - /* Reset format on source pad to sink pad format */ - format.pad = MSM_CSID_PAD_SRC; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - csid_set_format(&csid->subdev, NULL, &format); - } - - return 0; -} - -static const struct v4l2_subdev_core_ops csid_core_ops = { - .s_power = csid_set_power, -}; - -static const struct v4l2_subdev_video_ops csid_video_ops = { - .s_stream = csid_set_stream, -}; - -static const struct v4l2_subdev_pad_ops csid_pad_ops = { - .enum_mbus_code = csid_enum_mbus_code, - .enum_frame_size = csid_enum_frame_size, - .get_fmt = csid_get_format, - .set_fmt = csid_set_format, -}; - -static const struct v4l2_subdev_ops csid_v4l2_ops = { - .core = &csid_core_ops, - .video = &csid_video_ops, - .pad = &csid_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = { - .open = csid_init_formats, -}; - -static const struct media_entity_operations csid_media_ops = { - .link_setup = csid_link_setup, - .link_validate = v4l2_subdev_link_validate, -}; - -/* - * msm_csid_register_entity - Register subdev node for CSID module - * @csid: CSID device - * @v4l2_dev: V4L2 device - * - * Return 0 on success or a negative error code otherwise - */ -int msm_csid_register_entity(struct csid_device *csid, - struct v4l2_device *v4l2_dev) -{ - struct v4l2_subdev *sd = &csid->subdev; - struct media_pad *pads = csid->pads; - struct device *dev = to_device_index(csid, csid->id); - int ret; - - v4l2_subdev_init(sd, &csid_v4l2_ops); - sd->internal_ops = &csid_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", - MSM_CSID_NAME, csid->id); - v4l2_set_subdevdata(sd, csid); - - ret = v4l2_ctrl_handler_init(&csid->ctrls, 1); - if (ret < 0) { - dev_err(dev, "Failed to init ctrl handler: %d\n", ret); - return ret; - } - - csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, - &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0, - csid_test_pattern_menu); - - if (csid->ctrls.error) { - dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); - ret = csid->ctrls.error; - goto free_ctrl; - } - - csid->subdev.ctrl_handler = &csid->ctrls; - - ret = csid_init_formats(sd, NULL); - if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); - goto free_ctrl; - } - - pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - - sd->entity.function = MEDIA_ENT_F_IO_V4L; - sd->entity.ops = &csid_media_ops; - ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); - if (ret < 0) { - dev_err(dev, "Failed to init media entity: %d\n", ret); - goto free_ctrl; - } - - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); - goto media_cleanup; - } - - return 0; - -media_cleanup: - media_entity_cleanup(&sd->entity); -free_ctrl: - v4l2_ctrl_handler_free(&csid->ctrls); - - return ret; -} - -/* - * msm_csid_unregister_entity - Unregister CSID module subdev node - * @csid: CSID device - */ -void msm_csid_unregister_entity(struct csid_device *csid) -{ - v4l2_device_unregister_subdev(&csid->subdev); - media_entity_cleanup(&csid->subdev.entity); - v4l2_ctrl_handler_free(&csid->ctrls); -} diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss-8x16/camss-csid.h deleted file mode 100644 index 8682d3081bc3..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * camss-csid.h - * - * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module - * - * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_CSID_H -#define QC_MSM_CAMSS_CSID_H - -#include -#include -#include -#include -#include -#include - -#define MSM_CSID_PAD_SINK 0 -#define MSM_CSID_PAD_SRC 1 -#define MSM_CSID_PADS_NUM 2 - -enum csid_payload_mode { - CSID_PAYLOAD_MODE_INCREMENTING = 0, - CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1, - CSID_PAYLOAD_MODE_ALL_ZEROES = 2, - CSID_PAYLOAD_MODE_ALL_ONES = 3, - CSID_PAYLOAD_MODE_RANDOM = 4, - CSID_PAYLOAD_MODE_USER_SPECIFIED = 5, -}; - -struct csid_testgen_config { - u8 enabled; - enum csid_payload_mode payload_mode; -}; - -struct csid_phy_config { - u8 csiphy_id; - u8 lane_cnt; - u32 lane_assign; -}; - -struct csid_device { - u8 id; - struct v4l2_subdev subdev; - struct media_pad pads[MSM_CSID_PADS_NUM]; - void __iomem *base; - u32 irq; - char irq_name[30]; - struct camss_clock *clock; - int nclocks; - struct regulator *vdda; - struct completion reset_complete; - struct csid_testgen_config testgen; - struct csid_phy_config phy; - struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *testgen_mode; -}; - -struct resources; - -int msm_csid_subdev_init(struct csid_device *csid, - const struct resources *res, u8 id); - -int msm_csid_register_entity(struct csid_device *csid, - struct v4l2_device *v4l2_dev); - -void msm_csid_unregister_entity(struct csid_device *csid); - -void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); - -#endif /* QC_MSM_CAMSS_CSID_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c deleted file mode 100644 index 7e61caba6a2d..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * camss-csiphy.c - * - * Qualcomm MSM Camera Subsystem - CSIPHY Module - * - * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2016-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "camss-csiphy.h" -#include "camss.h" - -#define MSM_CSIPHY_NAME "msm_csiphy" - -#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) -#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) -#define CAMSS_CSI_PHY_GLBL_RESET 0x140 -#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 -#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 -#define CAMSS_CSI_PHY_HW_VERSION 0x188 -#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) -#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec -#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 - -static const struct { - u32 code; - u8 bpp; -} csiphy_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } -}; - -/* - * csiphy_get_bpp - map media bus format to bits per pixel - * @code: media bus format code - * - * Return number of bits per pixel - */ -static u8 csiphy_get_bpp(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (code == csiphy_formats[i].code) - return csiphy_formats[i].bpp; - - WARN(1, "Unknown format\n"); - - return csiphy_formats[0].bpp; -} - -/* - * csiphy_isr - CSIPHY module interrupt handler - * @irq: Interrupt line - * @dev: CSIPHY device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t csiphy_isr(int irq, void *dev) -{ - struct csiphy_device *csiphy = dev; - u8 i; - - for (i = 0; i < 8; i++) { - u8 val = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); - writel_relaxed(val, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - return IRQ_HANDLED; -} - -/* - * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module - * @csiphy: CSIPHY device - */ -static int csiphy_set_clock_rates(struct csiphy_device *csiphy) -{ - struct device *dev = to_device_index(csiphy, csiphy->id); - u32 pixel_clock; - int i, j; - int ret; - - ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); - if (ret) - pixel_clock = 0; - - for (i = 0; i < csiphy->nclocks; i++) { - struct camss_clock *clock = &csiphy->clock[i]; - - if (!strcmp(clock->name, "csiphy0_timer") || - !strcmp(clock->name, "csiphy1_timer")) { - u8 bpp = csiphy_get_bpp( - csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); - u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; - u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); - long round_rate; - - camss_add_clock_margin(&min_rate); - - for (j = 0; j < clock->nfreqs; j++) - if (min_rate < clock->freq[j]) - break; - - if (j == clock->nfreqs) { - dev_err(dev, - "Pixel clock is too high for CSIPHY\n"); - return -EINVAL; - } - - /* if sensor pixel clock is not available */ - /* set highest possible CSIPHY clock rate */ - if (min_rate == 0) - j = clock->nfreqs - 1; - - round_rate = clk_round_rate(clock->clk, clock->freq[j]); - if (round_rate < 0) { - dev_err(dev, "clk round rate failed: %ld\n", - round_rate); - return -EINVAL; - } - - csiphy->timer_clk_rate = round_rate; - - ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate); - if (ret < 0) { - dev_err(dev, "clk set rate failed: %d\n", ret); - return ret; - } - } - } - - return 0; -} - -/* - * csiphy_reset - Perform software reset on CSIPHY module - * @csiphy: CSIPHY device - */ -static void csiphy_reset(struct csiphy_device *csiphy) -{ - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - usleep_range(5000, 8000); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); -} - -/* - * csiphy_set_power - Power on/off CSIPHY module - * @sd: CSIPHY V4L2 subdevice - * @on: Requested power state - * - * Return 0 on success or a negative error code otherwise - */ -static int csiphy_set_power(struct v4l2_subdev *sd, int on) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csiphy, csiphy->id); - - if (on) { - u8 hw_version; - int ret; - - ret = csiphy_set_clock_rates(csiphy); - if (ret < 0) - return ret; - - ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); - if (ret < 0) - return ret; - - enable_irq(csiphy->irq); - - csiphy_reset(csiphy); - - hw_version = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_HW_VERSION); - dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); - } else { - disable_irq(csiphy->irq); - - camss_disable_clocks(csiphy->nclocks, csiphy->clock); - } - - return 0; -} - -/* - * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter - * @lane_cfg - CSI2 lane configuration - * - * Return lane mask - */ -static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) -{ - u8 lane_mask; - int i; - - lane_mask = 1 << lane_cfg->clk.pos; - - for (i = 0; i < lane_cfg->num_data; i++) - lane_mask |= 1 << lane_cfg->data[i].pos; - - return lane_mask; -} - -/* - * csiphy_settle_cnt_calc - Calculate settle count value - * @csiphy: CSIPHY device - * - * Helper function to calculate settle count value. This is - * based on the CSI2 T_hs_settle parameter which in turn - * is calculated based on the CSI2 transmitter pixel clock - * frequency. - * - * Return settle count value or 0 if the CSI2 pixel clock - * frequency is not available - */ -static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) -{ - u8 bpp = csiphy_get_bpp( - csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); - u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; - u32 pixel_clock; /* Hz */ - u32 mipi_clock; /* Hz */ - u32 ui; /* ps */ - u32 timer_period; /* ps */ - u32 t_hs_prepare_max; /* ps */ - u32 t_hs_prepare_zero_min; /* ps */ - u32 t_hs_settle; /* ps */ - u8 settle_cnt; - int ret; - - ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); - if (ret) { - dev_err(to_device_index(csiphy, csiphy->id), - "Cannot get CSI2 transmitter's pixel clock\n"); - return 0; - } - if (!pixel_clock) { - dev_err(to_device_index(csiphy, csiphy->id), - "Got pixel clock == 0, cannot continue\n"); - return 0; - } - - mipi_clock = pixel_clock * bpp / (2 * num_lanes); - ui = div_u64(1000000000000LL, mipi_clock); - ui /= 2; - t_hs_prepare_max = 85000 + 6 * ui; - t_hs_prepare_zero_min = 145000 + 10 * ui; - t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; - - timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate); - settle_cnt = t_hs_settle / timer_period; - - return settle_cnt; -} - -/* - * csiphy_stream_on - Enable streaming on CSIPHY module - * @csiphy: CSIPHY device - * - * Helper function to enable streaming on CSIPHY module. - * Main configuration of CSIPHY module is also done here. - * - * Return 0 on success or a negative error code otherwise - */ -static int csiphy_stream_on(struct csiphy_device *csiphy) -{ - struct csiphy_config *cfg = &csiphy->cfg; - u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); - u8 settle_cnt; - u8 val; - int i = 0; - - settle_cnt = csiphy_settle_cnt_calc(csiphy); - if (!settle_cnt) - return -EINVAL; - - val = readl_relaxed(csiphy->base_clk_mux); - if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { - val &= ~0xf0; - val |= cfg->csid_id << 4; - } else { - val &= ~0xf; - val |= cfg->csid_id; - } - writel_relaxed(val, csiphy->base_clk_mux); - - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_T_WAKEUP_CFG0); - - val = 0x1; - val |= lane_mask << 1; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); - - val = cfg->combo_mode << 4; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - - while (lane_mask) { - if (lane_mask & 0x1) { - writel_relaxed(0x10, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - writel_relaxed(settle_cnt, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG3(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - lane_mask >>= 1; - i++; - } - - return 0; -} - -/* - * csiphy_stream_off - Disable streaming on CSIPHY module - * @csiphy: CSIPHY device - * - * Helper function to disable streaming on CSIPHY module - */ -static void csiphy_stream_off(struct csiphy_device *csiphy) -{ - u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg); - int i = 0; - - while (lane_mask) { - if (lane_mask & 0x1) - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - - lane_mask >>= 1; - i++; - } - - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); -} - - -/* - * csiphy_set_stream - Enable/disable streaming on CSIPHY module - * @sd: CSIPHY V4L2 subdevice - * @enable: Requested streaming state - * - * Return 0 on success or a negative error code otherwise - */ -static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - int ret = 0; - - if (enable) - ret = csiphy_stream_on(csiphy); - else - csiphy_stream_off(csiphy); - - return ret; -} - -/* - * __csiphy_get_format - Get pointer to format structure - * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration - * @pad: pad from which format is requested - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE format structure - */ -static struct v4l2_mbus_framefmt * -__csiphy_get_format(struct csiphy_device *csiphy, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad); - - return &csiphy->fmt[pad]; -} - -/* - * csiphy_try_format - Handle try format by pad subdev method - * @csiphy: CSIPHY device - * @cfg: V4L2 subdev pad configuration - * @pad: pad on which format is requested - * @fmt: pointer to v4l2 format structure - * @which: wanted subdev format - */ -static void csiphy_try_format(struct csiphy_device *csiphy, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) -{ - unsigned int i; - - switch (pad) { - case MSM_CSIPHY_PAD_SINK: - /* Set format on sink pad */ - - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (fmt->code == csiphy_formats[i].code) - break; - - /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csiphy_formats)) - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - - fmt->width = clamp_t(u32, fmt->width, 1, 8191); - fmt->height = clamp_t(u32, fmt->height, 1, 8191); - - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - break; - - case MSM_CSIPHY_PAD_SRC: - /* Set and return a format same as sink pad */ - - *fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK, - which); - - break; - } -} - -/* - * csiphy_enum_mbus_code - Handle pixel format enumeration - * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * return -EINVAL or zero on success - */ -static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csiphy_formats)) - return -EINVAL; - - code->code = csiphy_formats[code->index].code; - } else { - if (code->index > 0) - return -EINVAL; - - format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK, - code->which); - - code->code = format->code; - } - - return 0; -} - -/* - * csiphy_enum_frame_size - Handle frame size enumeration - * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fse: pointer to v4l2_subdev_frame_size_enum structure - * return -EINVAL or zero on success - */ -static int csiphy_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt format; - - if (fse->index != 0) - return -EINVAL; - - format.code = fse->code; - format.width = 1; - format.height = 1; - csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); - fse->min_width = format.width; - fse->min_height = format.height; - - if (format.code != fse->code) - return -EINVAL; - - format.code = fse->code; - format.width = -1; - format.height = -1; - csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); - fse->max_width = format.width; - fse->max_height = format.height; - - return 0; -} - -/* - * csiphy_get_format - Handle get format by pads subdev method - * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int csiphy_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - fmt->format = *format; - - return 0; -} - -/* - * csiphy_set_format - Handle set format by pads subdev method - * @sd: CSIPHY V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int csiphy_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which); - *format = fmt->format; - - /* Propagate the format from sink to source */ - if (fmt->pad == MSM_CSIPHY_PAD_SINK) { - format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, - fmt->which); - - *format = fmt->format; - csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format, - fmt->which); - } - - return 0; -} - -/* - * csiphy_init_formats - Initialize formats on all pads - * @sd: CSIPHY V4L2 subdevice - * @fh: V4L2 subdev file handle - * - * Initialize all pad formats with default values. - * - * Return 0 on success or a negative error code otherwise - */ -static int csiphy_init_formats(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh) -{ - struct v4l2_subdev_format format = { - .pad = MSM_CSIPHY_PAD_SINK, - .which = fh ? V4L2_SUBDEV_FORMAT_TRY : - V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .width = 1920, - .height = 1080 - } - }; - - return csiphy_set_format(sd, fh ? fh->pad : NULL, &format); -} - -/* - * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources - * @csiphy: CSIPHY device - * @res: CSIPHY module resources table - * @id: CSIPHY module id - * - * Return 0 on success or a negative error code otherwise - */ -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, - const struct resources *res, u8 id) -{ - struct device *dev = to_device_index(csiphy, id); - struct platform_device *pdev = to_platform_device(dev); - struct resource *r; - int i, j; - int ret; - - csiphy->id = id; - csiphy->cfg.combo_mode = 0; - - /* Memory */ - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); - csiphy->base = devm_ioremap_resource(dev, r); - if (IS_ERR(csiphy->base)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(csiphy->base); - } - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); - csiphy->base_clk_mux = devm_ioremap_resource(dev, r); - if (IS_ERR(csiphy->base_clk_mux)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(csiphy->base_clk_mux); - } - - /* Interrupt */ - - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } - - csiphy->irq = r->start; - snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", - dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy_isr, - IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy); - if (ret < 0) { - dev_err(dev, "request_irq failed: %d\n", ret); - return ret; - } - - disable_irq(csiphy->irq); - - /* Clocks */ - - csiphy->nclocks = 0; - while (res->clock[csiphy->nclocks]) - csiphy->nclocks++; - - csiphy->clock = devm_kcalloc(dev, - csiphy->nclocks, sizeof(*csiphy->clock), - GFP_KERNEL); - if (!csiphy->clock) - return -ENOMEM; - - for (i = 0; i < csiphy->nclocks; i++) { - struct camss_clock *clock = &csiphy->clock[i]; - - clock->clk = devm_clk_get(dev, res->clock[i]); - if (IS_ERR(clock->clk)) - return PTR_ERR(clock->clk); - - clock->name = res->clock[i]; - - clock->nfreqs = 0; - while (res->clock_rate[i][clock->nfreqs]) - clock->nfreqs++; - - if (!clock->nfreqs) { - clock->freq = NULL; - continue; - } - - clock->freq = devm_kcalloc(dev, - clock->nfreqs, - sizeof(*clock->freq), - GFP_KERNEL); - if (!clock->freq) - return -ENOMEM; - - for (j = 0; j < clock->nfreqs; j++) - clock->freq[j] = res->clock_rate[i][j]; - } - - return 0; -} - -/* - * csiphy_link_setup - Setup CSIPHY connections - * @entity: Pointer to media entity structure - * @local: Pointer to local pad - * @remote: Pointer to remote pad - * @flags: Link flags - * - * Rreturn 0 on success - */ -static int csiphy_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) -{ - if ((local->flags & MEDIA_PAD_FL_SOURCE) && - (flags & MEDIA_LNK_FL_ENABLED)) { - struct v4l2_subdev *sd; - struct csiphy_device *csiphy; - struct csid_device *csid; - - if (media_entity_remote_pad(local)) - return -EBUSY; - - sd = media_entity_to_v4l2_subdev(entity); - csiphy = v4l2_get_subdevdata(sd); - - sd = media_entity_to_v4l2_subdev(remote->entity); - csid = v4l2_get_subdevdata(sd); - - csiphy->cfg.csid_id = csid->id; - } - - return 0; -} - -static const struct v4l2_subdev_core_ops csiphy_core_ops = { - .s_power = csiphy_set_power, -}; - -static const struct v4l2_subdev_video_ops csiphy_video_ops = { - .s_stream = csiphy_set_stream, -}; - -static const struct v4l2_subdev_pad_ops csiphy_pad_ops = { - .enum_mbus_code = csiphy_enum_mbus_code, - .enum_frame_size = csiphy_enum_frame_size, - .get_fmt = csiphy_get_format, - .set_fmt = csiphy_set_format, -}; - -static const struct v4l2_subdev_ops csiphy_v4l2_ops = { - .core = &csiphy_core_ops, - .video = &csiphy_video_ops, - .pad = &csiphy_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = { - .open = csiphy_init_formats, -}; - -static const struct media_entity_operations csiphy_media_ops = { - .link_setup = csiphy_link_setup, - .link_validate = v4l2_subdev_link_validate, -}; - -/* - * msm_csiphy_register_entity - Register subdev node for CSIPHY module - * @csiphy: CSIPHY device - * @v4l2_dev: V4L2 device - * - * Return 0 on success or a negative error code otherwise - */ -int msm_csiphy_register_entity(struct csiphy_device *csiphy, - struct v4l2_device *v4l2_dev) -{ - struct v4l2_subdev *sd = &csiphy->subdev; - struct media_pad *pads = csiphy->pads; - struct device *dev = to_device_index(csiphy, csiphy->id); - int ret; - - v4l2_subdev_init(sd, &csiphy_v4l2_ops); - sd->internal_ops = &csiphy_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", - MSM_CSIPHY_NAME, csiphy->id); - v4l2_set_subdevdata(sd, csiphy); - - ret = csiphy_init_formats(sd, NULL); - if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); - return ret; - } - - pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - - sd->entity.function = MEDIA_ENT_F_IO_V4L; - sd->entity.ops = &csiphy_media_ops; - ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); - if (ret < 0) { - dev_err(dev, "Failed to init media entity: %d\n", ret); - return ret; - } - - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); - media_entity_cleanup(&sd->entity); - } - - return ret; -} - -/* - * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node - * @csiphy: CSIPHY device - */ -void msm_csiphy_unregister_entity(struct csiphy_device *csiphy) -{ - v4l2_device_unregister_subdev(&csiphy->subdev); - media_entity_cleanup(&csiphy->subdev.entity); -} diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h deleted file mode 100644 index ba8781122065..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * camss-csiphy.h - * - * Qualcomm MSM Camera Subsystem - CSIPHY Module - * - * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2016-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_CSIPHY_H -#define QC_MSM_CAMSS_CSIPHY_H - -#include -#include -#include -#include -#include - -#define MSM_CSIPHY_PAD_SINK 0 -#define MSM_CSIPHY_PAD_SRC 1 -#define MSM_CSIPHY_PADS_NUM 2 - -struct csiphy_lane { - u8 pos; - u8 pol; -}; - -struct csiphy_lanes_cfg { - int num_data; - struct csiphy_lane *data; - struct csiphy_lane clk; -}; - -struct csiphy_csi2_cfg { - struct csiphy_lanes_cfg lane_cfg; -}; - -struct csiphy_config { - u8 combo_mode; - u8 csid_id; - struct csiphy_csi2_cfg *csi2; -}; - -struct csiphy_device { - u8 id; - struct v4l2_subdev subdev; - struct media_pad pads[MSM_CSIPHY_PADS_NUM]; - void __iomem *base; - void __iomem *base_clk_mux; - u32 irq; - char irq_name[30]; - struct camss_clock *clock; - int nclocks; - u32 timer_clk_rate; - struct csiphy_config cfg; - struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; -}; - -struct resources; - -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, - const struct resources *res, u8 id); - -int msm_csiphy_register_entity(struct csiphy_device *csiphy, - struct v4l2_device *v4l2_dev); - -void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); - -#endif /* QC_MSM_CAMSS_CSIPHY_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c deleted file mode 100644 index 9d1af9353c1d..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * camss-ispif.c - * - * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "camss-ispif.h" -#include "camss.h" - -#define MSM_ISPIF_NAME "msm_ispif" - -#define ispif_line_array(ptr_line) \ - ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)])) - -#define to_ispif(ptr_line) \ - container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line) - -#define ISPIF_RST_CMD_0 0x008 -#define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) -#define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) -#define ISPIF_RST_CMD_0_SW_REG_RST (1 << 2) -#define ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST (1 << 3) -#define ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST (1 << 4) -#define ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST (1 << 5) -#define ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST (1 << 6) -#define ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST (1 << 7) -#define ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST (1 << 8) -#define ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST (1 << 9) -#define ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST (1 << 10) -#define ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST (1 << 11) -#define ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST (1 << 12) -#define ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST (1 << 16) -#define ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST (1 << 17) -#define ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST (1 << 18) -#define ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST (1 << 19) -#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x01c -#define ISPIF_VFE_m_CTRL_0(m) (0x200 + 0x200 * (m)) -#define ISPIF_VFE_m_CTRL_0_PIX0_LINE_BUF_EN (1 << 6) -#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE 0x00001249 -#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK 0x00001fff -#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE 0x02492000 -#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK 0x03ffe000 -#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20c + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE 0x00001249 -#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK 0x00001fff -#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE 0x02492000 -#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK 0x03ffe000 -#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE 0x00001249 -#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK 0x00001fff -#define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x21c + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW (1 << 12) -#define ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW (1 << 25) -#define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x220 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW (1 << 12) -#define ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW (1 << 25) -#define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x224 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW (1 << 12) -#define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x230 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x234 + 0x200 * (m)) -#define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x238 + 0x200 * (m)) -#define ISPIF_VFE_m_INTF_INPUT_SEL(m) (0x244 + 0x200 * (m)) -#define ISPIF_VFE_m_INTF_CMD_0(m) (0x248 + 0x200 * (m)) -#define ISPIF_VFE_m_INTF_CMD_1(m) (0x24c + 0x200 * (m)) -#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) \ - (0x254 + 0x200 * (m) + 0x4 * (n)) -#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \ - (0x264 + 0x200 * (m) + 0x4 * (n)) -#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ - (0x2c0 + 0x200 * (m) + 0x4 * (n)) -#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ - (0x2d0 + 0x200 * (m) + 0x4 * (n)) - -#define CSI_PIX_CLK_MUX_SEL 0x000 -#define CSI_RDI_CLK_MUX_SEL 0x008 - -#define ISPIF_TIMEOUT_SLEEP_US 1000 -#define ISPIF_TIMEOUT_ALL_US 1000000 -#define ISPIF_RESET_TIMEOUT_MS 500 - -enum ispif_intf_cmd { - CMD_DISABLE_FRAME_BOUNDARY = 0x0, - CMD_ENABLE_FRAME_BOUNDARY = 0x1, - CMD_DISABLE_IMMEDIATELY = 0x2, - CMD_ALL_DISABLE_IMMEDIATELY = 0xaaaaaaaa, - CMD_ALL_NO_CHANGE = 0xffffffff, -}; - -static const u32 ispif_formats[] = { - MEDIA_BUS_FMT_UYVY8_2X8, - MEDIA_BUS_FMT_VYUY8_2X8, - MEDIA_BUS_FMT_YUYV8_2X8, - MEDIA_BUS_FMT_YVYU8_2X8, - MEDIA_BUS_FMT_SBGGR8_1X8, - MEDIA_BUS_FMT_SGBRG8_1X8, - MEDIA_BUS_FMT_SGRBG8_1X8, - MEDIA_BUS_FMT_SRGGB8_1X8, - MEDIA_BUS_FMT_SBGGR10_1X10, - MEDIA_BUS_FMT_SGBRG10_1X10, - MEDIA_BUS_FMT_SGRBG10_1X10, - MEDIA_BUS_FMT_SRGGB10_1X10, - MEDIA_BUS_FMT_SBGGR12_1X12, - MEDIA_BUS_FMT_SGBRG12_1X12, - MEDIA_BUS_FMT_SGRBG12_1X12, - MEDIA_BUS_FMT_SRGGB12_1X12, -}; - -/* - * ispif_isr - ISPIF module interrupt handler - * @irq: Interrupt line - * @dev: ISPIF device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t ispif_isr(int irq, void *dev) -{ - struct ispif_device *ispif = dev; - u32 value0, value1, value2; - - value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); - value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0)); - value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0)); - - writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0)); - writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0)); - writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0)); - - writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); - - if ((value0 >> 27) & 0x1) - complete(&ispif->reset_complete); - - if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); - - if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n"); - - if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n"); - - if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n"); - - if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n"); - - return IRQ_HANDLED; -} - -/* - * ispif_reset - Trigger reset on ISPIF module and wait to complete - * @ispif: ISPIF device - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_reset(struct ispif_device *ispif) -{ - unsigned long time; - u32 val; - int ret; - - ret = camss_enable_clocks(ispif->nclocks_for_reset, - ispif->clock_for_reset, - to_device(ispif)); - if (ret < 0) - return ret; - - reinit_completion(&ispif->reset_complete); - - val = ISPIF_RST_CMD_0_STROBED_RST_EN | - ISPIF_RST_CMD_0_MISC_LOGIC_RST | - ISPIF_RST_CMD_0_SW_REG_RST | - ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST | - ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST | - ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST | - ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST | - ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST | - ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST | - ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST | - ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST | - ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST | - ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST | - ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST | - ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST | - ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST | - ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST; - - writel_relaxed(val, ispif->base + ISPIF_RST_CMD_0); - - time = wait_for_completion_timeout(&ispif->reset_complete, - msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); - if (!time) { - dev_err(to_device(ispif), "ISPIF reset timeout\n"); - return -EIO; - } - - camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); - - return 0; -} - -/* - * ispif_set_power - Power on/off ISPIF module - * @sd: ISPIF V4L2 subdevice - * @on: Requested power state - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_set_power(struct v4l2_subdev *sd, int on) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); - struct device *dev = to_device(ispif); - int ret = 0; - - mutex_lock(&ispif->power_lock); - - if (on) { - if (ispif->power_count) { - /* Power is already on */ - ispif->power_count++; - goto exit; - } - - ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); - if (ret < 0) - goto exit; - - ret = ispif_reset(ispif); - if (ret < 0) { - camss_disable_clocks(ispif->nclocks, ispif->clock); - goto exit; - } - - ispif->intf_cmd[line->vfe_id].cmd_0 = CMD_ALL_NO_CHANGE; - ispif->intf_cmd[line->vfe_id].cmd_1 = CMD_ALL_NO_CHANGE; - - ispif->power_count++; - } else { - if (ispif->power_count == 0) { - dev_err(dev, "ispif power off on power_count == 0\n"); - goto exit; - } else if (ispif->power_count == 1) { - camss_disable_clocks(ispif->nclocks, ispif->clock); - } - - ispif->power_count--; - } - -exit: - mutex_unlock(&ispif->power_lock); - - return ret; -} - -/* - * ispif_select_clk_mux - Select clock for PIX/RDI interface - * @ispif: ISPIF device - * @intf: VFE interface - * @csid: CSID HW module id - * @vfe: VFE HW module id - * @enable: enable or disable the selected clock - */ -static void ispif_select_clk_mux(struct ispif_device *ispif, - enum ispif_intf intf, u8 csid, - u8 vfe, u8 enable) -{ - u32 val; - - switch (intf) { - case PIX0: - val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); - val &= ~(0xf << (vfe * 8)); - if (enable) - val |= (csid << (vfe * 8)); - writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); - break; - - case RDI0: - val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - val &= ~(0xf << (vfe * 12)); - if (enable) - val |= (csid << (vfe * 12)); - writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - break; - - case PIX1: - val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); - val &= ~(0xf << (4 + (vfe * 8))); - if (enable) - val |= (csid << (4 + (vfe * 8))); - writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); - break; - - case RDI1: - val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - val &= ~(0xf << (4 + (vfe * 12))); - if (enable) - val |= (csid << (4 + (vfe * 12))); - writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - break; - - case RDI2: - val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - val &= ~(0xf << (8 + (vfe * 12))); - if (enable) - val |= (csid << (8 + (vfe * 12))); - writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); - break; - } - - mb(); -} - -/* - * ispif_validate_intf_status - Validate current status of PIX/RDI interface - * @ispif: ISPIF device - * @intf: VFE interface - * @vfe: VFE HW module id - * - * Return 0 when interface is idle or -EBUSY otherwise - */ -static int ispif_validate_intf_status(struct ispif_device *ispif, - enum ispif_intf intf, u8 vfe) -{ - int ret = 0; - u32 val = 0; - - switch (intf) { - case PIX0: - val = readl_relaxed(ispif->base + - ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0)); - break; - case RDI0: - val = readl_relaxed(ispif->base + - ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0)); - break; - case PIX1: - val = readl_relaxed(ispif->base + - ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1)); - break; - case RDI1: - val = readl_relaxed(ispif->base + - ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1)); - break; - case RDI2: - val = readl_relaxed(ispif->base + - ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2)); - break; - } - - if ((val & 0xf) != 0xf) { - dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n", - __func__, val); - ret = -EBUSY; - } - - return ret; -} - -/* - * ispif_wait_for_stop - Wait for PIX/RDI interface to stop - * @ispif: ISPIF device - * @intf: VFE interface - * @vfe: VFE HW module id - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_wait_for_stop(struct ispif_device *ispif, - enum ispif_intf intf, u8 vfe) -{ - u32 addr = 0; - u32 stop_flag = 0; - int ret; - - switch (intf) { - case PIX0: - addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0); - break; - case RDI0: - addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0); - break; - case PIX1: - addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1); - break; - case RDI1: - addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1); - break; - case RDI2: - addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2); - break; - } - - ret = readl_poll_timeout(ispif->base + addr, - stop_flag, - (stop_flag & 0xf) == 0xf, - ISPIF_TIMEOUT_SLEEP_US, - ISPIF_TIMEOUT_ALL_US); - if (ret < 0) - dev_err(to_device(ispif), "%s: ispif stop timeout\n", - __func__); - - return ret; -} - -/* - * ispif_select_csid - Select CSID HW module for input from - * @ispif: ISPIF device - * @intf: VFE interface - * @csid: CSID HW module id - * @vfe: VFE HW module id - * @enable: enable or disable the selected input - */ -static void ispif_select_csid(struct ispif_device *ispif, enum ispif_intf intf, - u8 csid, u8 vfe, u8 enable) -{ - u32 val; - - val = readl_relaxed(ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); - switch (intf) { - case PIX0: - val &= ~(BIT(1) | BIT(0)); - if (enable) - val |= csid; - break; - case RDI0: - val &= ~(BIT(5) | BIT(4)); - if (enable) - val |= (csid << 4); - break; - case PIX1: - val &= ~(BIT(9) | BIT(8)); - if (enable) - val |= (csid << 8); - break; - case RDI1: - val &= ~(BIT(13) | BIT(12)); - if (enable) - val |= (csid << 12); - break; - case RDI2: - val &= ~(BIT(21) | BIT(20)); - if (enable) - val |= (csid << 20); - break; - } - - writel(val, ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); -} - -/* - * ispif_select_cid - Enable/disable desired CID - * @ispif: ISPIF device - * @intf: VFE interface - * @cid: desired CID to enable/disable - * @vfe: VFE HW module id - * @enable: enable or disable the desired CID - */ -static void ispif_select_cid(struct ispif_device *ispif, enum ispif_intf intf, - u8 cid, u8 vfe, u8 enable) -{ - u32 cid_mask = 1 << cid; - u32 addr = 0; - u32 val; - - switch (intf) { - case PIX0: - addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 0); - break; - case RDI0: - addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 0); - break; - case PIX1: - addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 1); - break; - case RDI1: - addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 1); - break; - case RDI2: - addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 2); - break; - } - - val = readl_relaxed(ispif->base + addr); - if (enable) - val |= cid_mask; - else - val &= ~cid_mask; - - writel(val, ispif->base + addr); -} - -/* - * ispif_config_irq - Enable/disable interrupts for PIX/RDI interface - * @ispif: ISPIF device - * @intf: VFE interface - * @vfe: VFE HW module id - * @enable: enable or disable - */ -static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf, - u8 vfe, u8 enable) -{ - u32 val; - - switch (intf) { - case PIX0: - val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); - val &= ~ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK; - if (enable) - val |= ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE; - writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); - writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE, - ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); - break; - case RDI0: - val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); - val &= ~ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK; - if (enable) - val |= ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE; - writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); - writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE, - ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); - break; - case PIX1: - val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); - val &= ~ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK; - if (enable) - val |= ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE; - writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); - writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE, - ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); - break; - case RDI1: - val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); - val &= ~ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK; - if (enable) - val |= ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE; - writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); - writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE, - ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); - break; - case RDI2: - val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); - val &= ~ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK; - if (enable) - val |= ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE; - writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); - writel_relaxed(ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE, - ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(vfe)); - break; - } - - writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); -} - -/* - * ispif_set_intf_cmd - Set command to enable/disable interface - * @ispif: ISPIF device - * @cmd: interface command - * @intf: VFE interface - * @vfe: VFE HW module id - * @vc: virtual channel - */ -static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd, - enum ispif_intf intf, u8 vfe, u8 vc) -{ - u32 *val; - - if (intf == RDI2) { - val = &ispif->intf_cmd[vfe].cmd_1; - *val &= ~(0x3 << (vc * 2 + 8)); - *val |= (cmd << (vc * 2 + 8)); - wmb(); - writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe)); - wmb(); - } else { - val = &ispif->intf_cmd[vfe].cmd_0; - *val &= ~(0x3 << (vc * 2 + intf * 8)); - *val |= (cmd << (vc * 2 + intf * 8)); - wmb(); - writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe)); - wmb(); - } -} - -/* - * ispif_set_stream - Enable/disable streaming on ISPIF module - * @sd: ISPIF V4L2 subdevice - * @enable: Requested streaming state - * - * Main configuration of ISPIF module is also done here. - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); - enum ispif_intf intf = line->interface; - u8 csid = line->csid_id; - u8 vfe = line->vfe_id; - u8 vc = 0; /* Virtual Channel 0 */ - u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ - int ret; - - if (enable) { - if (!media_entity_remote_pad(&line->pads[MSM_ISPIF_PAD_SINK])) - return -ENOLINK; - - /* Config */ - - mutex_lock(&ispif->config_lock); - ispif_select_clk_mux(ispif, intf, csid, vfe, 1); - - ret = ispif_validate_intf_status(ispif, intf, vfe); - if (ret < 0) { - mutex_unlock(&ispif->config_lock); - return ret; - } - - ispif_select_csid(ispif, intf, csid, vfe, 1); - ispif_select_cid(ispif, intf, cid, vfe, 1); - ispif_config_irq(ispif, intf, vfe, 1); - ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY, - intf, vfe, vc); - } else { - mutex_lock(&ispif->config_lock); - ispif_set_intf_cmd(ispif, CMD_DISABLE_FRAME_BOUNDARY, - intf, vfe, vc); - mutex_unlock(&ispif->config_lock); - - ret = ispif_wait_for_stop(ispif, intf, vfe); - if (ret < 0) - return ret; - - mutex_lock(&ispif->config_lock); - ispif_config_irq(ispif, intf, vfe, 0); - ispif_select_cid(ispif, intf, cid, vfe, 0); - ispif_select_csid(ispif, intf, csid, vfe, 0); - ispif_select_clk_mux(ispif, intf, csid, vfe, 0); - } - - mutex_unlock(&ispif->config_lock); - - return 0; -} - -/* - * __ispif_get_format - Get pointer to format structure - * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration - * @pad: pad from which format is requested - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE format structure - */ -static struct v4l2_mbus_framefmt * -__ispif_get_format(struct ispif_line *line, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); - - return &line->fmt[pad]; -} - -/* - * ispif_try_format - Handle try format by pad subdev method - * @ispif: ISPIF line - * @cfg: V4L2 subdev pad configuration - * @pad: pad on which format is requested - * @fmt: pointer to v4l2 format structure - * @which: wanted subdev format - */ -static void ispif_try_format(struct ispif_line *line, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) -{ - unsigned int i; - - switch (pad) { - case MSM_ISPIF_PAD_SINK: - /* Set format on sink pad */ - - for (i = 0; i < ARRAY_SIZE(ispif_formats); i++) - if (fmt->code == ispif_formats[i]) - break; - - /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(ispif_formats)) - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - - fmt->width = clamp_t(u32, fmt->width, 1, 8191); - fmt->height = clamp_t(u32, fmt->height, 1, 8191); - - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - break; - - case MSM_ISPIF_PAD_SRC: - /* Set and return a format same as sink pad */ - - *fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, - which); - - break; - } - - fmt->colorspace = V4L2_COLORSPACE_SRGB; -} - -/* - * ispif_enum_mbus_code - Handle pixel format enumeration - * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * return -EINVAL or zero on success - */ -static int ispif_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - if (code->pad == MSM_ISPIF_PAD_SINK) { - if (code->index >= ARRAY_SIZE(ispif_formats)) - return -EINVAL; - - code->code = ispif_formats[code->index]; - } else { - if (code->index > 0) - return -EINVAL; - - format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, - code->which); - - code->code = format->code; - } - - return 0; -} - -/* - * ispif_enum_frame_size - Handle frame size enumeration - * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fse: pointer to v4l2_subdev_frame_size_enum structure - * return -EINVAL or zero on success - */ -static int ispif_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt format; - - if (fse->index != 0) - return -EINVAL; - - format.code = fse->code; - format.width = 1; - format.height = 1; - ispif_try_format(line, cfg, fse->pad, &format, fse->which); - fse->min_width = format.width; - fse->min_height = format.height; - - if (format.code != fse->code) - return -EINVAL; - - format.code = fse->code; - format.width = -1; - format.height = -1; - ispif_try_format(line, cfg, fse->pad, &format, fse->which); - fse->max_width = format.width; - fse->max_height = format.height; - - return 0; -} - -/* - * ispif_get_format - Handle get format by pads subdev method - * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int ispif_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - fmt->format = *format; - - return 0; -} - -/* - * ispif_set_format - Handle set format by pads subdev method - * @sd: ISPIF V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int ispif_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct ispif_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); - *format = fmt->format; - - /* Propagate the format from sink to source */ - if (fmt->pad == MSM_ISPIF_PAD_SINK) { - format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC, - fmt->which); - - *format = fmt->format; - ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format, - fmt->which); - } - - return 0; -} - -/* - * ispif_init_formats - Initialize formats on all pads - * @sd: ISPIF V4L2 subdevice - * @fh: V4L2 subdev file handle - * - * Initialize all pad formats with default values. - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_subdev_format format = { - .pad = MSM_ISPIF_PAD_SINK, - .which = fh ? V4L2_SUBDEV_FORMAT_TRY : - V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .width = 1920, - .height = 1080 - } - }; - - return ispif_set_format(sd, fh ? fh->pad : NULL, &format); -} - -/* - * msm_ispif_subdev_init - Initialize ISPIF device structure and resources - * @ispif: ISPIF device - * @res: ISPIF module resources table - * - * Return 0 on success or a negative error code otherwise - */ -int msm_ispif_subdev_init(struct ispif_device *ispif, - const struct resources_ispif *res) -{ - struct device *dev = to_device(ispif); - struct platform_device *pdev = to_platform_device(dev); - struct resource *r; - int i; - int ret; - - /* Memory */ - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); - ispif->base = devm_ioremap_resource(dev, r); - if (IS_ERR(ispif->base)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(ispif->base); - } - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); - ispif->base_clk_mux = devm_ioremap_resource(dev, r); - if (IS_ERR(ispif->base_clk_mux)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(ispif->base_clk_mux); - } - - /* Interrupt */ - - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt); - - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } - - ispif->irq = r->start; - snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", - dev_name(dev), MSM_ISPIF_NAME); - ret = devm_request_irq(dev, ispif->irq, ispif_isr, - IRQF_TRIGGER_RISING, ispif->irq_name, ispif); - if (ret < 0) { - dev_err(dev, "request_irq failed: %d\n", ret); - return ret; - } - - /* Clocks */ - - ispif->nclocks = 0; - while (res->clock[ispif->nclocks]) - ispif->nclocks++; - - ispif->clock = devm_kcalloc(dev, - ispif->nclocks, sizeof(*ispif->clock), - GFP_KERNEL); - if (!ispif->clock) - return -ENOMEM; - - for (i = 0; i < ispif->nclocks; i++) { - struct camss_clock *clock = &ispif->clock[i]; - - clock->clk = devm_clk_get(dev, res->clock[i]); - if (IS_ERR(clock->clk)) - return PTR_ERR(clock->clk); - - clock->freq = NULL; - clock->nfreqs = 0; - } - - ispif->nclocks_for_reset = 0; - while (res->clock_for_reset[ispif->nclocks_for_reset]) - ispif->nclocks_for_reset++; - - ispif->clock_for_reset = devm_kcalloc(dev, - ispif->nclocks_for_reset, - sizeof(*ispif->clock_for_reset), - GFP_KERNEL); - if (!ispif->clock_for_reset) - return -ENOMEM; - - for (i = 0; i < ispif->nclocks_for_reset; i++) { - struct camss_clock *clock = &ispif->clock_for_reset[i]; - - clock->clk = devm_clk_get(dev, res->clock_for_reset[i]); - if (IS_ERR(clock->clk)) - return PTR_ERR(clock->clk); - - clock->freq = NULL; - clock->nfreqs = 0; - } - - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) - ispif->line[i].id = i; - - mutex_init(&ispif->power_lock); - ispif->power_count = 0; - - mutex_init(&ispif->config_lock); - - init_completion(&ispif->reset_complete); - - return 0; -} - -/* - * ispif_get_intf - Get ISPIF interface to use by VFE line id - * @line_id: VFE line id that the ISPIF line is connected to - * - * Return ISPIF interface to use - */ -static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id) -{ - switch (line_id) { - case (VFE_LINE_RDI0): - return RDI0; - case (VFE_LINE_RDI1): - return RDI1; - case (VFE_LINE_RDI2): - return RDI2; - case (VFE_LINE_PIX): - return PIX0; - default: - return RDI0; - } -} - -/* - * ispif_link_setup - Setup ISPIF connections - * @entity: Pointer to media entity structure - * @local: Pointer to local pad - * @remote: Pointer to remote pad - * @flags: Link flags - * - * Return 0 on success - */ -static int ispif_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) -{ - if (flags & MEDIA_LNK_FL_ENABLED) { - if (media_entity_remote_pad(local)) - return -EBUSY; - - if (local->flags & MEDIA_PAD_FL_SINK) { - struct v4l2_subdev *sd; - struct ispif_line *line; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - - msm_csid_get_csid_id(remote->entity, &line->csid_id); - } else { /* MEDIA_PAD_FL_SOURCE */ - struct v4l2_subdev *sd; - struct ispif_line *line; - enum vfe_line_id id; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - - msm_vfe_get_vfe_id(remote->entity, &line->vfe_id); - msm_vfe_get_vfe_line_id(remote->entity, &id); - line->interface = ispif_get_intf(id); - } - } - - return 0; -} - -static const struct v4l2_subdev_core_ops ispif_core_ops = { - .s_power = ispif_set_power, -}; - -static const struct v4l2_subdev_video_ops ispif_video_ops = { - .s_stream = ispif_set_stream, -}; - -static const struct v4l2_subdev_pad_ops ispif_pad_ops = { - .enum_mbus_code = ispif_enum_mbus_code, - .enum_frame_size = ispif_enum_frame_size, - .get_fmt = ispif_get_format, - .set_fmt = ispif_set_format, -}; - -static const struct v4l2_subdev_ops ispif_v4l2_ops = { - .core = &ispif_core_ops, - .video = &ispif_video_ops, - .pad = &ispif_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops ispif_v4l2_internal_ops = { - .open = ispif_init_formats, -}; - -static const struct media_entity_operations ispif_media_ops = { - .link_setup = ispif_link_setup, - .link_validate = v4l2_subdev_link_validate, -}; - -/* - * msm_ispif_register_entities - Register subdev node for ISPIF module - * @ispif: ISPIF device - * @v4l2_dev: V4L2 device - * - * Return 0 on success or a negative error code otherwise - */ -int msm_ispif_register_entities(struct ispif_device *ispif, - struct v4l2_device *v4l2_dev) -{ - struct device *dev = to_device(ispif); - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { - struct v4l2_subdev *sd = &ispif->line[i].subdev; - struct media_pad *pads = ispif->line[i].pads; - - v4l2_subdev_init(sd, &ispif_v4l2_ops); - sd->internal_ops = &ispif_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", - MSM_ISPIF_NAME, i); - v4l2_set_subdevdata(sd, &ispif->line[i]); - - ret = ispif_init_formats(sd, NULL); - if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); - goto error; - } - - pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - - sd->entity.function = MEDIA_ENT_F_IO_V4L; - sd->entity.ops = &ispif_media_ops; - ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, - pads); - if (ret < 0) { - dev_err(dev, "Failed to init media entity: %d\n", ret); - goto error; - } - - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); - media_entity_cleanup(&sd->entity); - goto error; - } - } - - return 0; - -error: - for (i--; i >= 0; i--) { - struct v4l2_subdev *sd = &ispif->line[i].subdev; - - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - } - - return ret; -} - -/* - * msm_ispif_unregister_entities - Unregister ISPIF module subdev node - * @ispif: ISPIF device - */ -void msm_ispif_unregister_entities(struct ispif_device *ispif) -{ - int i; - - mutex_destroy(&ispif->power_lock); - mutex_destroy(&ispif->config_lock); - - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { - struct v4l2_subdev *sd = &ispif->line[i].subdev; - - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - } -} diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h deleted file mode 100644 index f668306020c3..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * camss-ispif.h - * - * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module - * - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_ISPIF_H -#define QC_MSM_CAMSS_ISPIF_H - -#include -#include -#include -#include - -/* Number of ISPIF lines - same as number of CSID hardware modules */ -#define MSM_ISPIF_LINE_NUM 2 - -#define MSM_ISPIF_PAD_SINK 0 -#define MSM_ISPIF_PAD_SRC 1 -#define MSM_ISPIF_PADS_NUM 2 - -#define MSM_ISPIF_VFE_NUM 1 - -enum ispif_intf { - PIX0, - RDI0, - PIX1, - RDI1, - RDI2 -}; - -struct ispif_intf_cmd_reg { - u32 cmd_0; - u32 cmd_1; -}; - -struct ispif_line { - u8 id; - u8 csid_id; - u8 vfe_id; - enum ispif_intf interface; - struct v4l2_subdev subdev; - struct media_pad pads[MSM_ISPIF_PADS_NUM]; - struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM]; -}; - -struct ispif_device { - void __iomem *base; - void __iomem *base_clk_mux; - u32 irq; - char irq_name[30]; - struct camss_clock *clock; - int nclocks; - struct camss_clock *clock_for_reset; - int nclocks_for_reset; - struct completion reset_complete; - int power_count; - struct mutex power_lock; - struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM]; - struct mutex config_lock; - struct ispif_line line[MSM_ISPIF_LINE_NUM]; -}; - -struct resources_ispif; - -int msm_ispif_subdev_init(struct ispif_device *ispif, - const struct resources_ispif *res); - -int msm_ispif_register_entities(struct ispif_device *ispif, - struct v4l2_device *v4l2_dev); - -void msm_ispif_unregister_entities(struct ispif_device *ispif); - -#endif /* QC_MSM_CAMSS_ISPIF_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c deleted file mode 100644 index a6329a8a7c4a..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c +++ /dev/null @@ -1,3093 +0,0 @@ -/* - * camss-vfe.c - * - * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "camss-vfe.h" -#include "camss.h" - -#define MSM_VFE_NAME "msm_vfe" - -#define vfe_line_array(ptr_line) \ - ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) - -#define to_vfe(ptr_line) \ - container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line) - -#define VFE_0_HW_VERSION 0x000 - -#define VFE_0_GLOBAL_RESET_CMD 0x00c -#define VFE_0_GLOBAL_RESET_CMD_CORE (1 << 0) -#define VFE_0_GLOBAL_RESET_CMD_CAMIF (1 << 1) -#define VFE_0_GLOBAL_RESET_CMD_BUS (1 << 2) -#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG (1 << 3) -#define VFE_0_GLOBAL_RESET_CMD_REGISTER (1 << 4) -#define VFE_0_GLOBAL_RESET_CMD_TIMER (1 << 5) -#define VFE_0_GLOBAL_RESET_CMD_PM (1 << 6) -#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR (1 << 7) -#define VFE_0_GLOBAL_RESET_CMD_TESTGEN (1 << 8) - -#define VFE_0_MODULE_CFG 0x018 -#define VFE_0_MODULE_CFG_DEMUX (1 << 2) -#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE (1 << 3) -#define VFE_0_MODULE_CFG_SCALE_ENC (1 << 23) -#define VFE_0_MODULE_CFG_CROP_ENC (1 << 27) - -#define VFE_0_CORE_CFG 0x01c -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 - -#define VFE_0_IRQ_CMD 0x024 -#define VFE_0_IRQ_CMD_GLOBAL_CLEAR (1 << 0) - -#define VFE_0_IRQ_MASK_0 0x028 -#define VFE_0_IRQ_MASK_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_MASK_0_CAMIF_EOF (1 << 1) -#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_MASK_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_MASK_1 0x02c -#define VFE_0_IRQ_MASK_1_CAMIF_ERROR (1 << 0) -#define VFE_0_IRQ_MASK_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) (1 << ((n) + 9)) -#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_CLEAR_0 0x030 -#define VFE_0_IRQ_CLEAR_1 0x034 - -#define VFE_0_IRQ_STATUS_0 0x038 -#define VFE_0_IRQ_STATUS_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_STATUS_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_STATUS_1 0x03c -#define VFE_0_IRQ_STATUS_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 -#define VFE_0_VIOLATION_STATUS 0x48 - -#define VFE_0_BUS_CMD 0x4c -#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) (1 << (x)) - -#define VFE_0_BUS_CFG 0x050 - -#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN (1 << 1) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1F << 2) - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ - (0x088 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ - (0x08c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff - -#define VFE_0_BUS_PING_PONG_STATUS 0x268 - -#define VFE_0_BUS_BDG_CMD 0x2c0 -#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 - -#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 -#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 -#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 -#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc -#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 -#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 -#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 -#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc -#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 -#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 - -#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) -#define VFE_0_RDI_CFG_x_RDI_EN_BIT (1 << 2) -#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 -#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) (1 << (16 + (r))) - -#define VFE_0_CAMIF_CMD 0x2f4 -#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 -#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 -#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2) -#define VFE_0_CAMIF_CFG 0x2f8 -#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6) -#define VFE_0_CAMIF_FRAME_CFG 0x300 -#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 -#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 -#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c -#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 -#define VFE_0_CAMIF_STATUS 0x31c -#define VFE_0_CAMIF_STATUS_HALT (1 << 31) - -#define VFE_0_REG_UPDATE 0x378 -#define VFE_0_REG_UPDATE_RDIn(n) (1 << (1 + (n))) -#define VFE_0_REG_UPDATE_line_n(n) \ - ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) - -#define VFE_0_DEMUX_CFG 0x424 -#define VFE_0_DEMUX_CFG_PERIOD 0x3 -#define VFE_0_DEMUX_GAIN_0 0x428 -#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) -#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) -#define VFE_0_DEMUX_GAIN_1 0x42c -#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) -#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) -#define VFE_0_DEMUX_EVEN_CFG 0x438 -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 -#define VFE_0_DEMUX_ODD_CFG 0x43c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 - -#define VFE_0_SCALE_ENC_Y_CFG 0x75c -#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 -#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 -#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c -#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 -#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 -#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c -#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 -#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 -#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 - -#define VFE_0_CROP_ENC_Y_WIDTH 0x854 -#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 -#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c -#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 - -#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 -#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) -#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 -#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) - -#define VFE_0_CGC_OVERRIDE_1 0x974 -#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) (1 << (x)) - -/* VFE reset timeout */ -#define VFE_RESET_TIMEOUT_MS 50 -/* VFE halt timeout */ -#define VFE_HALT_TIMEOUT_MS 100 -/* Max number of frame drop updates per frame */ -#define VFE_FRAME_DROP_UPDATES 5 -/* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */ -#define VFE_FRAME_DROP_VAL 20 - -#define VFE_NEXT_SOF_MS 500 - -#define CAMIF_TIMEOUT_SLEEP_US 1000 -#define CAMIF_TIMEOUT_ALL_US 1000000 - -#define SCALER_RATIO_MAX 16 - -static const struct { - u32 code; - u8 bpp; -} vfe_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } -}; - -/* - * vfe_get_bpp - map media bus format to bits per pixel - * @code: media bus format code - * - * Return number of bits per pixel - */ -static u8 vfe_get_bpp(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (code == vfe_formats[i].code) - return vfe_formats[i].bpp; - - WARN(1, "Unknown format\n"); - - return vfe_formats[0].bpp; -} - -static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits & ~clr_bits, vfe->base + reg); -} - -static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits | set_bits, vfe->base + reg); -} - -static void vfe_global_reset(struct vfe_device *vfe) -{ - u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | - VFE_0_GLOBAL_RESET_CMD_BUS_MISR | - VFE_0_GLOBAL_RESET_CMD_PM | - VFE_0_GLOBAL_RESET_CMD_TIMER | - VFE_0_GLOBAL_RESET_CMD_REGISTER | - VFE_0_GLOBAL_RESET_CMD_BUS_BDG | - VFE_0_GLOBAL_RESET_CMD_BUS | - VFE_0_GLOBAL_RESET_CMD_CAMIF | - VFE_0_GLOBAL_RESET_CMD_CORE; - - writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); -} - -static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); -} - -static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); -} - -#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) - -static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line) -{ - int val = 0; - - switch (format) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - val = CALC_WORD(pixel_per_line, 1, 8); - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - val = CALC_WORD(pixel_per_line, 2, 8); - break; - } - - return val; -} - -static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, - u16 *width, u16 *height, u16 *bytesperline) -{ - switch (pix->pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - if (plane == 1) - *height /= 2; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - break; - } -} - -static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, - struct v4l2_pix_format_mplane *pix, - u8 plane, u32 enable) -{ - u32 reg; - - if (enable) { - u16 width = 0, height = 0, bytesperline = 0, wpl; - - vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); - - wpl = vfe_word_per_line(pix->pixelformat, width); - - reg = height - 1; - reg |= ((wpl + 1) / 2 - 1) << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - - wpl = vfe_word_per_line(pix->pixelformat, bytesperline); - - reg = 0x3; - reg |= (height - 1) << 4; - reg |= wpl << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } else { - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } -} - -static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); - - reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); - - reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) - & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; - - writel_relaxed(reg, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); -} - -static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, - u32 pattern) -{ - writel_relaxed(pattern, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); -} - -static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset, - u16 depth) -{ - u32 reg; - - reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | - depth; - writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); -} - -static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) -{ - wmb(); - writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); - wmb(); -} - -static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); -} - -static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); -} - -static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); - - return (reg >> wm) & 0x1; -} - -static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) -{ - if (enable) - writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); - else - writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); -} - -static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & - VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} - -static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) -{ - writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, - vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); -} - -static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} - -static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, - u8 enable) -{ - struct vfe_line *line = container_of(output, struct vfe_line, output); - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (i == 0) { - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - } else if (i == 1) { - reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) - reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; - } else { - /* On current devices output->wm_num is always <= 2 */ - break; - } - - if (output->wm_idx[i] % 2 == 1) - reg <<= 16; - - if (enable) - vfe_reg_set(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - else - vfe_reg_clr(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - } -} - -static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) -{ - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), - VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); - - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), - cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); -} - -static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); - wmb(); - writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); - wmb(); -} - -static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, - enum vfe_line_id line_id, u8 enable) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | - VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | - VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } -} - -static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, - enum vfe_line_id line_id, u8 enable) -{ - struct vfe_output *output = &vfe->line[line_id].output; - unsigned int i; - u32 irq_en0; - u32 irq_en1; - u32 comp_mask = 0; - - irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; - irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; - irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); - irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; - for (i = 0; i < output->wm_num; i++) { - irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( - output->wm_idx[i]); - comp_mask |= (1 << output->wm_idx[i]) << comp * 8; - } - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } -} - -static void vfe_enable_irq_common(struct vfe_device *vfe) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; - u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | - VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; - - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); -} - -static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val, even_cfg, odd_cfg; - - writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); - - val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); - - val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); - - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; - break; - } - - writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); - writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); -} - -static inline u8 vfe_calc_interp_reso(u16 input, u16 output) -{ - if (input / output >= 16) - return 0; - - if (input / output >= 8) - return 1; - - if (input / output >= 4) - return 2; - - return 3; -} - -static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 input, output; - u8 interp_reso; - u32 phase_mult; - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) - output = line->compose.height / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); -} - -static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 first, last; - - first = line->crop.left; - last = line->crop.left + line->crop.width - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); - - first = line->crop.left / 2; - last = line->crop.left / 2 + line->crop.width / 2 - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { - first = line->crop.top / 2; - last = line->crop.top / 2 + line->crop.height / 2 - 1; - } - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); -} - -static void vfe_set_clamp_cfg(struct vfe_device *vfe) -{ - u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | - VFE_0_CLAMP_ENC_MAX_CFG_CH1 | - VFE_0_CLAMP_ENC_MAX_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); - - val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | - VFE_0_CLAMP_ENC_MIN_CFG_CH1 | - VFE_0_CLAMP_ENC_MIN_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); -} - -/* - * vfe_reset - Trigger reset on VFE module and wait to complete - * @vfe: VFE device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_reset(struct vfe_device *vfe) -{ - unsigned long time; - - reinit_completion(&vfe->reset_complete); - - vfe_global_reset(vfe); - - time = wait_for_completion_timeout(&vfe->reset_complete, - msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); - if (!time) { - dev_err(to_device(vfe), "VFE reset timeout\n"); - return -EIO; - } - - return 0; -} - -/* - * vfe_halt - Trigger halt on VFE module and wait to complete - * @vfe: VFE device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_halt(struct vfe_device *vfe) -{ - unsigned long time; - - reinit_completion(&vfe->halt_complete); - - writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, - vfe->base + VFE_0_BUS_BDG_CMD); - - time = wait_for_completion_timeout(&vfe->halt_complete, - msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); - if (!time) { - dev_err(to_device(vfe), "VFE halt timeout\n"); - return -EIO; - } - - return 0; -} - -static void vfe_init_outputs(struct vfe_device *vfe) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { - struct vfe_output *output = &vfe->line[i].output; - - output->state = VFE_OUTPUT_OFF; - output->buf[0] = NULL; - output->buf[1] = NULL; - INIT_LIST_HEAD(&output->pending_bufs); - - output->wm_num = 1; - if (vfe->line[i].id == VFE_LINE_PIX) - output->wm_num = 2; - } -} - -static void vfe_reset_output_maps(struct vfe_device *vfe) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) - vfe->wm_output_map[i] = VFE_LINE_NONE; -} - -static void vfe_set_qos(struct vfe_device *vfe) -{ - u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; - u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; - - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); - writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); -} - -static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) -{ - u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); - - if (enable) - vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); - else - vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); - - wmb(); -} - -static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) -{ - u32 val = VFE_0_MODULE_CFG_DEMUX | - VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | - VFE_0_MODULE_CFG_SCALE_ENC | - VFE_0_MODULE_CFG_CROP_ENC; - - if (enable) - writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); - else - writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); -} - -static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val; - - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; - break; - } - - writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2; - val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].height - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); - - val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); - - val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); -} - -static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd) -{ - writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS, - vfe->base + VFE_0_CAMIF_CMD); - - writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); -} - -static int vfe_camif_wait_for_stop(struct vfe_device *vfe) -{ - u32 val; - int ret; - - ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, - val, - (val & VFE_0_CAMIF_STATUS_HALT), - CAMIF_TIMEOUT_SLEEP_US, - CAMIF_TIMEOUT_ALL_US); - if (ret < 0) - dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__); - - return ret; -} - -static void vfe_output_init_addrs(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 ping_addr; - u32 pong_addr; - unsigned int i; - - output->active_buf = 0; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[0]) - ping_addr = output->buf[0]->addr[i]; - else - ping_addr = 0; - - if (output->buf[1]) - pong_addr = output->buf[1]->addr[i]; - else - pong_addr = ping_addr; - - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); - if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); - } -} - -static void vfe_output_update_ping_addr(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 addr; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[0]) - addr = output->buf[0]->addr[i]; - else - addr = 0; - - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr); - if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); - } -} - -static void vfe_output_update_pong_addr(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 addr; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[1]) - addr = output->buf[1]->addr[i]; - else - addr = 0; - - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr); - if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); - } - -} - -static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - int ret = -EBUSY; - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) { - if (vfe->wm_output_map[i] == VFE_LINE_NONE) { - vfe->wm_output_map[i] = line_id; - ret = i; - break; - } - } - - return ret; -} - -static int vfe_release_wm(struct vfe_device *vfe, u8 wm) -{ - if (wm >= ARRAY_SIZE(vfe->wm_output_map)) - return -EINVAL; - - vfe->wm_output_map[wm] = VFE_LINE_NONE; - - return 0; -} - -static void vfe_output_frame_drop(struct vfe_device *vfe, - struct vfe_output *output, - u32 drop_pattern) -{ - u8 drop_period; - unsigned int i; - - /* We need to toggle update period to be valid on next frame */ - output->drop_update_idx++; - output->drop_update_idx %= VFE_FRAME_DROP_UPDATES; - drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; - - for (i = 0; i < output->wm_num; i++) { - vfe_wm_set_framedrop_period(vfe, output->wm_idx[i], - drop_period); - vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i], - drop_pattern); - } - vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id); -} - -static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) -{ - struct camss_buffer *buffer = NULL; - - if (!list_empty(&output->pending_bufs)) { - buffer = list_first_entry(&output->pending_bufs, - struct camss_buffer, - queue); - list_del(&buffer->queue); - } - - return buffer; -} - -/* - * vfe_buf_add_pending - Add output buffer to list of pending - * @output: VFE output - * @buffer: Video buffer - */ -static void vfe_buf_add_pending(struct vfe_output *output, - struct camss_buffer *buffer) -{ - INIT_LIST_HEAD(&buffer->queue); - list_add_tail(&buffer->queue, &output->pending_bufs); -} - -/* - * vfe_buf_flush_pending - Flush all pending buffers. - * @output: VFE output - * @state: vb2 buffer state - */ -static void vfe_buf_flush_pending(struct vfe_output *output, - enum vb2_buffer_state state) -{ - struct camss_buffer *buf; - struct camss_buffer *t; - - list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { - vb2_buffer_done(&buf->vb.vb2_buf, state); - list_del(&buf->queue); - } -} - -static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, - struct vfe_output *output) -{ - switch (output->state) { - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3); - break; - case VFE_OUTPUT_SINGLE: - default: - dev_err_ratelimited(to_device(vfe), - "Next buf in wrong state! %d\n", - output->state); - break; - } -} - -static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, - struct vfe_output *output) -{ - switch (output->state) { - case VFE_OUTPUT_CONTINUOUS: - output->state = VFE_OUTPUT_SINGLE; - vfe_output_frame_drop(vfe, output, 1); - break; - case VFE_OUTPUT_SINGLE: - output->state = VFE_OUTPUT_STOPPING; - vfe_output_frame_drop(vfe, output, 0); - break; - default: - dev_err_ratelimited(to_device(vfe), - "Last buff in wrong state! %d\n", - output->state); - break; - } -} - -static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, - struct vfe_output *output, - struct camss_buffer *new_buf) -{ - int inactive_idx; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - inactive_idx = !output->active_buf; - - if (!output->buf[inactive_idx]) { - output->buf[inactive_idx] = new_buf; - - if (inactive_idx) - vfe_output_update_pong_addr(vfe, output, 0); - else - vfe_output_update_ping_addr(vfe, output, 0); - - vfe_output_frame_drop(vfe, output, 3); - output->state = VFE_OUTPUT_CONTINUOUS; - } else { - vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), - "Inactive buffer is busy\n"); - } - break; - - case VFE_OUTPUT_IDLE: - if (!output->buf[0]) { - output->buf[0] = new_buf; - - vfe_output_init_addrs(vfe, output, 1); - - vfe_output_frame_drop(vfe, output, 1); - output->state = VFE_OUTPUT_SINGLE; - } else { - vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), - "Output idle with buffer set!\n"); - } - break; - - case VFE_OUTPUT_CONTINUOUS: - default: - vfe_buf_add_pending(output, new_buf); - break; - } -} - -static int vfe_get_output(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - struct vfe_output *output; - unsigned long flags; - int i; - int wm_idx; - - spin_lock_irqsave(&vfe->output_lock, flags); - - output = &line->output; - if (output->state != VFE_OUTPUT_OFF) { - dev_err(to_device(vfe), "Output is running\n"); - goto error; - } - output->state = VFE_OUTPUT_RESERVED; - - output->active_buf = 0; - - for (i = 0; i < output->wm_num; i++) { - wm_idx = vfe_reserve_wm(vfe, line->id); - if (wm_idx < 0) { - dev_err(to_device(vfe), "Can not reserve wm\n"); - goto error_get_wm; - } - output->wm_idx[i] = wm_idx; - } - - output->drop_update_idx = 0; - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; - -error_get_wm: - for (i--; i >= 0; i--) - vfe_release_wm(vfe, output->wm_idx[i]); - output->state = VFE_OUTPUT_OFF; -error: - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return -EINVAL; -} - -static int vfe_put_output(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - struct vfe_output *output = &line->output; - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&vfe->output_lock, flags); - - for (i = 0; i < output->wm_num; i++) - vfe_release_wm(vfe, output->wm_idx[i]); - - output->state = VFE_OUTPUT_OFF; - - spin_unlock_irqrestore(&vfe->output_lock, flags); - return 0; -} - -static int vfe_enable_output(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - struct vfe_output *output = &line->output; - unsigned long flags; - unsigned int i; - u16 ub_size; - - switch (vfe->id) { - case 0: - ub_size = MSM_VFE_VFE0_UB_SIZE_RDI; - break; - case 1: - ub_size = MSM_VFE_VFE1_UB_SIZE_RDI; - break; - default: - return -EINVAL; - } - - spin_lock_irqsave(&vfe->output_lock, flags); - - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id); - - if (output->state != VFE_OUTPUT_RESERVED) { - dev_err(to_device(vfe), "Output is not in reserved state %d\n", - output->state); - spin_unlock_irqrestore(&vfe->output_lock, flags); - return -EINVAL; - } - output->state = VFE_OUTPUT_IDLE; - - output->buf[0] = vfe_buf_get_pending(output); - output->buf[1] = vfe_buf_get_pending(output); - - if (!output->buf[0] && output->buf[1]) { - output->buf[0] = output->buf[1]; - output->buf[1] = NULL; - } - - if (output->buf[0]) - output->state = VFE_OUTPUT_SINGLE; - - if (output->buf[1]) - output->state = VFE_OUTPUT_CONTINUOUS; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - vfe_output_frame_drop(vfe, output, 1); - break; - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3); - break; - default: - vfe_output_frame_drop(vfe, output, 0); - break; - } - - output->sequence = 0; - output->wait_sof = 0; - output->wait_reg_update = 0; - reinit_completion(&output->sof); - reinit_completion(&output->reg_update); - - vfe_output_init_addrs(vfe, output, 0); - - if (line->id != VFE_LINE_PIX) { - vfe_set_cgc_override(vfe, output->wm_idx[0], 1); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); - vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); - vfe_wm_set_subsample(vfe, output->wm_idx[0]); - vfe_set_rdi_cid(vfe, line->id, 0); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[0], - (ub_size + 1) * output->wm_idx[0], ub_size); - vfe_wm_frame_based(vfe, output->wm_idx[0], 1); - vfe_wm_enable(vfe, output->wm_idx[0], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[0]); - } else { - ub_size /= output->wm_num; - for (i = 0; i < output->wm_num; i++) { - vfe_set_cgc_override(vfe, output->wm_idx[i], 1); - vfe_wm_set_subsample(vfe, output->wm_idx[i]); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[i], - (ub_size + 1) * output->wm_idx[i], - ub_size); - vfe_wm_line_based(vfe, output->wm_idx[i], - &line->video_out.active_fmt.fmt.pix_mp, - i, 1); - vfe_wm_enable(vfe, output->wm_idx[i], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[i]); - } - vfe_enable_irq_pix_line(vfe, 0, line->id, 1); - vfe_set_module_cfg(vfe, 1); - vfe_set_camif_cfg(vfe, line); - vfe_set_xbar_cfg(vfe, output, 1); - vfe_set_demux_cfg(vfe, line); - vfe_set_scale_cfg(vfe, line); - vfe_set_crop_cfg(vfe, line); - vfe_set_clamp_cfg(vfe); - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY); - } - - vfe_reg_update(vfe, line->id); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; -} - -static int vfe_disable_output(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - struct vfe_output *output = &line->output; - unsigned long flags; - unsigned long time; - unsigned int i; - - spin_lock_irqsave(&vfe->output_lock, flags); - - output->wait_sof = 1; - spin_unlock_irqrestore(&vfe->output_lock, flags); - - time = wait_for_completion_timeout(&output->sof, - msecs_to_jiffies(VFE_NEXT_SOF_MS)); - if (!time) - dev_err(to_device(vfe), "VFE sof timeout\n"); - - spin_lock_irqsave(&vfe->output_lock, flags); - for (i = 0; i < output->wm_num; i++) - vfe_wm_enable(vfe, output->wm_idx[i], 0); - - vfe_reg_update(vfe, line->id); - output->wait_reg_update = 1; - spin_unlock_irqrestore(&vfe->output_lock, flags); - - time = wait_for_completion_timeout(&output->reg_update, - msecs_to_jiffies(VFE_NEXT_SOF_MS)); - if (!time) - dev_err(to_device(vfe), "VFE reg update timeout\n"); - - spin_lock_irqsave(&vfe->output_lock, flags); - - if (line->id != VFE_LINE_PIX) { - vfe_wm_frame_based(vfe, output->wm_idx[0], 0); - vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); - vfe_set_cgc_override(vfe, output->wm_idx[0], 0); - spin_unlock_irqrestore(&vfe->output_lock, flags); - } else { - for (i = 0; i < output->wm_num; i++) { - vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); - vfe_set_cgc_override(vfe, output->wm_idx[i], 0); - } - - vfe_enable_irq_pix_line(vfe, 0, line->id, 0); - vfe_set_module_cfg(vfe, 0); - vfe_set_xbar_cfg(vfe, output, 0); - - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY); - spin_unlock_irqrestore(&vfe->output_lock, flags); - - vfe_camif_wait_for_stop(vfe); - } - - return 0; -} - -/* - * vfe_enable - Enable streaming on VFE line - * @line: VFE line - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_enable(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - int ret; - - mutex_lock(&vfe->stream_lock); - - if (!vfe->stream_count) { - vfe_enable_irq_common(vfe); - - vfe_bus_enable_wr_if(vfe, 1); - - vfe_set_qos(vfe); - } - - vfe->stream_count++; - - mutex_unlock(&vfe->stream_lock); - - ret = vfe_get_output(line); - if (ret < 0) - goto error_get_output; - - ret = vfe_enable_output(line); - if (ret < 0) - goto error_enable_output; - - vfe->was_streaming = 1; - - return 0; - - -error_enable_output: - vfe_put_output(line); - -error_get_output: - mutex_lock(&vfe->stream_lock); - - if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); - - vfe->stream_count--; - - mutex_unlock(&vfe->stream_lock); - - return ret; -} - -/* - * vfe_disable - Disable streaming on VFE line - * @line: VFE line - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_disable(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - - vfe_disable_output(line); - - vfe_put_output(line); - - mutex_lock(&vfe->stream_lock); - - if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); - - vfe->stream_count--; - - mutex_unlock(&vfe->stream_lock); - - return 0; -} - -/* - * vfe_isr_sof - Process start of frame interrupt - * @vfe: VFE Device - * @line_id: VFE line - */ -static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - struct vfe_output *output; - unsigned long flags; - - spin_lock_irqsave(&vfe->output_lock, flags); - output = &vfe->line[line_id].output; - if (output->wait_sof) { - output->wait_sof = 0; - complete(&output->sof); - } - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_reg_update - Process reg update interrupt - * @vfe: VFE Device - * @line_id: VFE line - */ -static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - struct vfe_output *output; - unsigned long flags; - - spin_lock_irqsave(&vfe->output_lock, flags); - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); - - output = &vfe->line[line_id].output; - - if (output->wait_reg_update) { - output->wait_reg_update = 0; - complete(&output->reg_update); - spin_unlock_irqrestore(&vfe->output_lock, flags); - return; - } - - if (output->state == VFE_OUTPUT_STOPPING) { - /* Release last buffer when hw is idle */ - if (output->last_buffer) { - vb2_buffer_done(&output->last_buffer->vb.vb2_buf, - VB2_BUF_STATE_DONE); - output->last_buffer = NULL; - } - output->state = VFE_OUTPUT_IDLE; - - /* Buffers received in stopping state are queued in */ - /* dma pending queue, start next capture here */ - - output->buf[0] = vfe_buf_get_pending(output); - output->buf[1] = vfe_buf_get_pending(output); - - if (!output->buf[0] && output->buf[1]) { - output->buf[0] = output->buf[1]; - output->buf[1] = NULL; - } - - if (output->buf[0]) - output->state = VFE_OUTPUT_SINGLE; - - if (output->buf[1]) - output->state = VFE_OUTPUT_CONTINUOUS; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - vfe_output_frame_drop(vfe, output, 2); - break; - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3); - break; - default: - vfe_output_frame_drop(vfe, output, 0); - break; - } - - vfe_output_init_addrs(vfe, output, 1); - } - - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_wm_done - Process write master done interrupt - * @vfe: VFE Device - * @wm: Write master id - */ -static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) -{ - struct camss_buffer *ready_buf; - struct vfe_output *output; - dma_addr_t *new_addr; - unsigned long flags; - u32 active_index; - u64 ts = ktime_get_ns(); - unsigned int i; - - active_index = vfe_wm_get_ping_pong_status(vfe, wm); - - spin_lock_irqsave(&vfe->output_lock, flags); - - if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { - dev_err_ratelimited(to_device(vfe), - "Received wm done for unmapped index\n"); - goto out_unlock; - } - output = &vfe->line[vfe->wm_output_map[wm]].output; - - if (output->active_buf == active_index) { - dev_err_ratelimited(to_device(vfe), - "Active buffer mismatch!\n"); - goto out_unlock; - } - output->active_buf = active_index; - - ready_buf = output->buf[!active_index]; - if (!ready_buf) { - dev_err_ratelimited(to_device(vfe), - "Missing ready buf %d %d!\n", - !active_index, output->state); - goto out_unlock; - } - - ready_buf->vb.vb2_buf.timestamp = ts; - ready_buf->vb.sequence = output->sequence++; - - /* Get next buffer */ - output->buf[!active_index] = vfe_buf_get_pending(output); - if (!output->buf[!active_index]) { - /* No next buffer - set same address */ - new_addr = ready_buf->addr; - vfe_buf_update_wm_on_last(vfe, output); - } else { - new_addr = output->buf[!active_index]->addr; - vfe_buf_update_wm_on_next(vfe, output); - } - - if (active_index) - for (i = 0; i < output->wm_num; i++) - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], - new_addr[i]); - else - for (i = 0; i < output->wm_num; i++) - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], - new_addr[i]); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - if (output->state == VFE_OUTPUT_STOPPING) - output->last_buffer = ready_buf; - else - vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - - return; - -out_unlock: - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_wm_done - Process composite image done interrupt - * @vfe: VFE Device - * @comp: Composite image id - */ -static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) - if (vfe->wm_output_map[i] == VFE_LINE_PIX) { - vfe_isr_wm_done(vfe, i); - break; - } -} - -/* - * vfe_isr - ISPIF module interrupt handler - * @irq: Interrupt line - * @dev: VFE device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t vfe_isr(int irq, void *dev) -{ - struct vfe_device *vfe = dev; - u32 value0, value1; - u32 violation; - int i, j; - - value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); - value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); - - writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0); - writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1); - - wmb(); - writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); - - if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) - complete(&vfe->reset_complete); - - if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) { - violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); - dev_err_ratelimited(to_device(vfe), - "VFE: violation = 0x%08x\n", violation); - } - - if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) { - complete(&vfe->halt_complete); - writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); - } - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) - if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) - vfe_isr_reg_update(vfe, i); - - if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) - vfe_isr_sof(vfe, VFE_LINE_PIX); - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) - if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) - vfe_isr_sof(vfe, i); - - for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { - vfe_isr_comp_done(vfe, i); - for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) - if (vfe->wm_output_map[j] == VFE_LINE_PIX) - value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); - } - - for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) - vfe_isr_wm_done(vfe, i); - - return IRQ_HANDLED; -} - -/* - * vfe_set_clock_rates - Calculate and set clock rates on VFE module - * @vfe: VFE device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_set_clock_rates(struct vfe_device *vfe) -{ - struct device *dev = to_device(vfe); - u32 pixel_clock[MSM_VFE_LINE_NUM]; - int i, j; - int ret; - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { - ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, - &pixel_clock[i]); - if (ret) - pixel_clock[i] = 0; - } - - for (i = 0; i < vfe->nclocks; i++) { - struct camss_clock *clock = &vfe->clock[i]; - - if (!strcmp(clock->name, "camss_vfe_vfe")) { - u64 min_rate = 0; - long rate; - - for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { - u32 tmp; - u8 bpp; - - if (j == VFE_LINE_PIX) { - tmp = pixel_clock[j]; - } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); - tmp = pixel_clock[j] * bpp / 64; - } - - if (min_rate < tmp) - min_rate = tmp; - } - - camss_add_clock_margin(&min_rate); - - for (j = 0; j < clock->nfreqs; j++) - if (min_rate < clock->freq[j]) - break; - - if (j == clock->nfreqs) { - dev_err(dev, - "Pixel clock is too high for VFE"); - return -EINVAL; - } - - /* if sensor pixel clock is not available */ - /* set highest possible VFE clock rate */ - if (min_rate == 0) - j = clock->nfreqs - 1; - - rate = clk_round_rate(clock->clk, clock->freq[j]); - if (rate < 0) { - dev_err(dev, "clk round rate failed: %ld\n", - rate); - return -EINVAL; - } - - ret = clk_set_rate(clock->clk, rate); - if (ret < 0) { - dev_err(dev, "clk set rate failed: %d\n", ret); - return ret; - } - } - } - - return 0; -} - -/* - * vfe_check_clock_rates - Check current clock rates on VFE module - * @vfe: VFE device - * - * Return 0 if current clock rates are suitable for a new pipeline - * or a negative error code otherwise - */ -static int vfe_check_clock_rates(struct vfe_device *vfe) -{ - u32 pixel_clock[MSM_VFE_LINE_NUM]; - int i, j; - int ret; - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { - ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, - &pixel_clock[i]); - if (ret) - pixel_clock[i] = 0; - } - - for (i = 0; i < vfe->nclocks; i++) { - struct camss_clock *clock = &vfe->clock[i]; - - if (!strcmp(clock->name, "camss_vfe_vfe")) { - u64 min_rate = 0; - unsigned long rate; - - for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { - u32 tmp; - u8 bpp; - - if (j == VFE_LINE_PIX) { - tmp = pixel_clock[j]; - } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); - tmp = pixel_clock[j] * bpp / 64; - } - - if (min_rate < tmp) - min_rate = tmp; - } - - camss_add_clock_margin(&min_rate); - - rate = clk_get_rate(clock->clk); - if (rate < min_rate) - return -EBUSY; - } - } - - return 0; -} - -/* - * vfe_get - Power up and reset VFE module - * @vfe: VFE Device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_get(struct vfe_device *vfe) -{ - int ret; - - mutex_lock(&vfe->power_lock); - - if (vfe->power_count == 0) { - ret = vfe_set_clock_rates(vfe); - if (ret < 0) - goto error_clocks; - - ret = camss_enable_clocks(vfe->nclocks, vfe->clock, - to_device(vfe)); - if (ret < 0) - goto error_clocks; - - ret = vfe_reset(vfe); - if (ret < 0) - goto error_reset; - - vfe_reset_output_maps(vfe); - - vfe_init_outputs(vfe); - } else { - ret = vfe_check_clock_rates(vfe); - if (ret < 0) - goto error_clocks; - } - vfe->power_count++; - - mutex_unlock(&vfe->power_lock); - - return 0; - -error_reset: - camss_disable_clocks(vfe->nclocks, vfe->clock); - -error_clocks: - mutex_unlock(&vfe->power_lock); - - return ret; -} - -/* - * vfe_put - Power down VFE module - * @vfe: VFE Device - */ -static void vfe_put(struct vfe_device *vfe) -{ - mutex_lock(&vfe->power_lock); - - if (vfe->power_count == 0) { - dev_err(to_device(vfe), "vfe power off on power_count == 0\n"); - goto exit; - } else if (vfe->power_count == 1) { - if (vfe->was_streaming) { - vfe->was_streaming = 0; - vfe_halt(vfe); - } - camss_disable_clocks(vfe->nclocks, vfe->clock); - } - - vfe->power_count--; - -exit: - mutex_unlock(&vfe->power_lock); -} - -/* - * vfe_video_pad_to_line - Get pointer to VFE line by media pad - * @pad: Media pad - * - * Return pointer to vfe line structure - */ -static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) -{ - struct media_pad *vfe_pad; - struct v4l2_subdev *subdev; - - vfe_pad = media_entity_remote_pad(pad); - if (vfe_pad == NULL) - return NULL; - - subdev = media_entity_to_v4l2_subdev(vfe_pad->entity); - - return container_of(subdev, struct vfe_line, subdev); -} - -/* - * vfe_queue_buffer - Add empty buffer - * @vid: Video device structure - * @buf: Buffer to be enqueued - * - * Add an empty buffer - depending on the current number of buffers it will be - * put in pending buffer queue or directly given to the hardware to be filled. - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_queue_buffer(struct camss_video *vid, - struct camss_buffer *buf) -{ - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; - struct vfe_output *output; - unsigned long flags; - - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not queue buffer\n"); - return -1; - } - output = &line->output; - - spin_lock_irqsave(&vfe->output_lock, flags); - - vfe_buf_update_wm_on_new(vfe, output, buf); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; -} - -/* - * vfe_flush_buffers - Return all vb2 buffers - * @vid: Video device structure - * @state: vb2 buffer state of the returned buffers - * - * Return all buffers to vb2. This includes queued pending buffers (still - * unused) and any buffers given to the hardware but again still not used. - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_flush_buffers(struct camss_video *vid, - enum vb2_buffer_state state) -{ - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; - struct vfe_output *output; - unsigned long flags; - - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not flush buffers\n"); - return -1; - } - output = &line->output; - - spin_lock_irqsave(&vfe->output_lock, flags); - - vfe_buf_flush_pending(output, state); - - if (output->buf[0]) - vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); - - if (output->buf[1]) - vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); - - if (output->last_buffer) { - vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); - output->last_buffer = NULL; - } - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; -} - -/* - * vfe_set_power - Power on/off VFE module - * @sd: VFE V4L2 subdevice - * @on: Requested power state - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_set_power(struct v4l2_subdev *sd, int on) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct vfe_device *vfe = to_vfe(line); - int ret; - - if (on) { - u32 hw_version; - - ret = vfe_get(vfe); - if (ret < 0) - return ret; - - hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); - dev_dbg(to_device(vfe), - "VFE HW Version = 0x%08x\n", hw_version); - } else { - vfe_put(vfe); - } - - return 0; -} - -/* - * vfe_set_stream - Enable/disable streaming on VFE module - * @sd: VFE V4L2 subdevice - * @enable: Requested streaming state - * - * Main configuration of VFE module is triggered here. - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct vfe_device *vfe = to_vfe(line); - int ret; - - if (enable) { - ret = vfe_enable(line); - if (ret < 0) - dev_err(to_device(vfe), - "Failed to enable vfe outputs\n"); - } else { - ret = vfe_disable(line); - if (ret < 0) - dev_err(to_device(vfe), - "Failed to disable vfe outputs\n"); - } - - return ret; -} - -/* - * __vfe_get_format - Get pointer to format structure - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @pad: pad from which format is requested - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE format structure - */ -static struct v4l2_mbus_framefmt * -__vfe_get_format(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); - - return &line->fmt[pad]; -} - -/* - * __vfe_get_compose - Get pointer to compose selection structure - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE compose rectangle structure - */ -static struct v4l2_rect * -__vfe_get_compose(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_compose(&line->subdev, cfg, - MSM_VFE_PAD_SINK); - - return &line->compose; -} - -/* - * __vfe_get_crop - Get pointer to crop selection structure - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @which: TRY or ACTIVE format - * - * Return pointer to TRY or ACTIVE crop rectangle structure - */ -static struct v4l2_rect * -__vfe_get_crop(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&line->subdev, cfg, - MSM_VFE_PAD_SRC); - - return &line->crop; -} - -/* - * vfe_try_format - Handle try format by pad subdev method - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @pad: pad on which format is requested - * @fmt: pointer to v4l2 format structure - * @which: wanted subdev format - */ -static void vfe_try_format(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, - struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) -{ - unsigned int i; - u32 code; - - switch (pad) { - case MSM_VFE_PAD_SINK: - /* Set format on sink pad */ - - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (fmt->code == vfe_formats[i].code) - break; - - /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(vfe_formats)) - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - - fmt->width = clamp_t(u32, fmt->width, 1, 8191); - fmt->height = clamp_t(u32, fmt->height, 1, 8191); - - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - - break; - - case MSM_VFE_PAD_SRC: - /* Set and return a format same as sink pad */ - - code = fmt->code; - - *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - which); - - if (line->id == VFE_LINE_PIX) { - struct v4l2_rect *rect; - - rect = __vfe_get_crop(line, cfg, which); - - fmt->width = rect->width; - fmt->height = rect->height; - - switch (fmt->code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - if (code == MEDIA_BUS_FMT_YUYV8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - if (code == MEDIA_BUS_FMT_YVYU8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YVYU8_2X8; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - if (code == MEDIA_BUS_FMT_UYVY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - if (code == MEDIA_BUS_FMT_VYUY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_VYUY8_2X8; - break; - } - } - - break; - } - - fmt->colorspace = V4L2_COLORSPACE_SRGB; -} - -/* - * vfe_try_compose - Handle try compose selection by pad subdev method - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @rect: pointer to v4l2 rect structure - * @which: wanted subdev format - */ -static void vfe_try_compose(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_rect *rect, - enum v4l2_subdev_format_whence which) -{ - struct v4l2_mbus_framefmt *fmt; - - fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); - - if (rect->width > fmt->width) - rect->width = fmt->width; - - if (rect->height > fmt->height) - rect->height = fmt->height; - - if (fmt->width > rect->width * SCALER_RATIO_MAX) - rect->width = (fmt->width + SCALER_RATIO_MAX - 1) / - SCALER_RATIO_MAX; - - rect->width &= ~0x1; - - if (fmt->height > rect->height * SCALER_RATIO_MAX) - rect->height = (fmt->height + SCALER_RATIO_MAX - 1) / - SCALER_RATIO_MAX; - - if (rect->width < 16) - rect->width = 16; - - if (rect->height < 4) - rect->height = 4; -} - -/* - * vfe_try_crop - Handle try crop selection by pad subdev method - * @line: VFE line - * @cfg: V4L2 subdev pad configuration - * @rect: pointer to v4l2 rect structure - * @which: wanted subdev format - */ -static void vfe_try_crop(struct vfe_line *line, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_rect *rect, - enum v4l2_subdev_format_whence which) -{ - struct v4l2_rect *compose; - - compose = __vfe_get_compose(line, cfg, which); - - if (rect->width > compose->width) - rect->width = compose->width; - - if (rect->width + rect->left > compose->width) - rect->left = compose->width - rect->width; - - if (rect->height > compose->height) - rect->height = compose->height; - - if (rect->height + rect->top > compose->height) - rect->top = compose->height - rect->height; - - /* wm in line based mode writes multiple of 16 horizontally */ - rect->left += (rect->width & 0xf) >> 1; - rect->width &= ~0xf; - - if (rect->width < 16) { - rect->left = 0; - rect->width = 16; - } - - if (rect->height < 4) { - rect->top = 0; - rect->height = 4; - } -} - -/* - * vfe_enum_mbus_code - Handle pixel format enumeration - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * return -EINVAL or zero on success - */ -static int vfe_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - if (code->pad == MSM_VFE_PAD_SINK) { - if (code->index >= ARRAY_SIZE(vfe_formats)) - return -EINVAL; - - code->code = vfe_formats[code->index].code; - } else { - if (code->index > 0) - return -EINVAL; - - format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - code->which); - - code->code = format->code; - } - - return 0; -} - -/* - * vfe_enum_frame_size - Handle frame size enumeration - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fse: pointer to v4l2_subdev_frame_size_enum structure - * - * Return -EINVAL or zero on success - */ -static int vfe_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt format; - - if (fse->index != 0) - return -EINVAL; - - format.code = fse->code; - format.width = 1; - format.height = 1; - vfe_try_format(line, cfg, fse->pad, &format, fse->which); - fse->min_width = format.width; - fse->min_height = format.height; - - if (format.code != fse->code) - return -EINVAL; - - format.code = fse->code; - format.width = -1; - format.height = -1; - vfe_try_format(line, cfg, fse->pad, &format, fse->which); - fse->max_width = format.width; - fse->max_height = format.height; - - return 0; -} - -/* - * vfe_get_format - Handle get format by pads subdev method - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int vfe_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - fmt->format = *format; - - return 0; -} - -static int vfe_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel); - -/* - * vfe_set_format - Handle set format by pads subdev method - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @fmt: pointer to v4l2 subdev format structure - * - * Return -EINVAL or zero on success - */ -static int vfe_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - - format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); - if (format == NULL) - return -EINVAL; - - vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); - *format = fmt->format; - - if (fmt->pad == MSM_VFE_PAD_SINK) { - struct v4l2_subdev_selection sel = { 0 }; - int ret; - - /* Propagate the format from sink to source */ - format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC, - fmt->which); - - *format = fmt->format; - vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format, - fmt->which); - - if (line->id != VFE_LINE_PIX) - return 0; - - /* Reset sink pad compose selection */ - sel.which = fmt->which; - sel.pad = MSM_VFE_PAD_SINK; - sel.target = V4L2_SEL_TGT_COMPOSE; - sel.r.width = fmt->format.width; - sel.r.height = fmt->format.height; - ret = vfe_set_selection(sd, cfg, &sel); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * vfe_get_selection - Handle get selection by pads subdev method - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @sel: pointer to v4l2 subdev selection structure - * - * Return -EINVAL or zero on success - */ -static int vfe_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_subdev_format fmt = { 0 }; - struct v4l2_rect *rect; - int ret; - - if (line->id != VFE_LINE_PIX) - return -EINVAL; - - if (sel->pad == MSM_VFE_PAD_SINK) - switch (sel->target) { - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - fmt.pad = sel->pad; - fmt.which = sel->which; - ret = vfe_get_format(sd, cfg, &fmt); - if (ret < 0) - return ret; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = fmt.format.width; - sel->r.height = fmt.format.height; - break; - case V4L2_SEL_TGT_COMPOSE: - rect = __vfe_get_compose(line, cfg, sel->which); - if (rect == NULL) - return -EINVAL; - - sel->r = *rect; - break; - default: - return -EINVAL; - } - else if (sel->pad == MSM_VFE_PAD_SRC) - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - rect = __vfe_get_compose(line, cfg, sel->which); - if (rect == NULL) - return -EINVAL; - - sel->r.left = rect->left; - sel->r.top = rect->top; - sel->r.width = rect->width; - sel->r.height = rect->height; - break; - case V4L2_SEL_TGT_CROP: - rect = __vfe_get_crop(line, cfg, sel->which); - if (rect == NULL) - return -EINVAL; - - sel->r = *rect; - break; - default: - return -EINVAL; - } - - return 0; -} - -/* - * vfe_set_selection - Handle set selection by pads subdev method - * @sd: VFE V4L2 subdevice - * @cfg: V4L2 subdev pad configuration - * @sel: pointer to v4l2 subdev selection structure - * - * Return -EINVAL or zero on success - */ -static int vfe_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_rect *rect; - int ret; - - if (line->id != VFE_LINE_PIX) - return -EINVAL; - - if (sel->target == V4L2_SEL_TGT_COMPOSE && - sel->pad == MSM_VFE_PAD_SINK) { - struct v4l2_subdev_selection crop = { 0 }; - - rect = __vfe_get_compose(line, cfg, sel->which); - if (rect == NULL) - return -EINVAL; - - vfe_try_compose(line, cfg, &sel->r, sel->which); - *rect = sel->r; - - /* Reset source crop selection */ - crop.which = sel->which; - crop.pad = MSM_VFE_PAD_SRC; - crop.target = V4L2_SEL_TGT_CROP; - crop.r = *rect; - ret = vfe_set_selection(sd, cfg, &crop); - } else if (sel->target == V4L2_SEL_TGT_CROP && - sel->pad == MSM_VFE_PAD_SRC) { - struct v4l2_subdev_format fmt = { 0 }; - - rect = __vfe_get_crop(line, cfg, sel->which); - if (rect == NULL) - return -EINVAL; - - vfe_try_crop(line, cfg, &sel->r, sel->which); - *rect = sel->r; - - /* Reset source pad format width and height */ - fmt.which = sel->which; - fmt.pad = MSM_VFE_PAD_SRC; - ret = vfe_get_format(sd, cfg, &fmt); - if (ret < 0) - return ret; - - fmt.format.width = rect->width; - fmt.format.height = rect->height; - ret = vfe_set_format(sd, cfg, &fmt); - } else { - ret = -EINVAL; - } - - return ret; -} - -/* - * vfe_init_formats - Initialize formats on all pads - * @sd: VFE V4L2 subdevice - * @fh: V4L2 subdev file handle - * - * Initialize all pad formats with default values. - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_subdev_format format = { - .pad = MSM_VFE_PAD_SINK, - .which = fh ? V4L2_SUBDEV_FORMAT_TRY : - V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .width = 1920, - .height = 1080 - } - }; - - return vfe_set_format(sd, fh ? fh->pad : NULL, &format); -} - -/* - * msm_vfe_subdev_init - Initialize VFE device structure and resources - * @vfe: VFE device - * @res: VFE module resources table - * - * Return 0 on success or a negative error code otherwise - */ -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) -{ - struct device *dev = to_device(vfe); - struct platform_device *pdev = to_platform_device(dev); - struct resource *r; - struct camss *camss = to_camss(vfe); - int i, j; - int ret; - - /* Memory */ - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); - vfe->base = devm_ioremap_resource(dev, r); - if (IS_ERR(vfe->base)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(vfe->base); - } - - /* Interrupt */ - - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } - - vfe->irq = r->start; - snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", - dev_name(dev), MSM_VFE_NAME, vfe->id); - ret = devm_request_irq(dev, vfe->irq, vfe_isr, - IRQF_TRIGGER_RISING, vfe->irq_name, vfe); - if (ret < 0) { - dev_err(dev, "request_irq failed: %d\n", ret); - return ret; - } - - /* Clocks */ - - vfe->nclocks = 0; - while (res->clock[vfe->nclocks]) - vfe->nclocks++; - - vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock), - GFP_KERNEL); - if (!vfe->clock) - return -ENOMEM; - - for (i = 0; i < vfe->nclocks; i++) { - struct camss_clock *clock = &vfe->clock[i]; - - clock->clk = devm_clk_get(dev, res->clock[i]); - if (IS_ERR(clock->clk)) - return PTR_ERR(clock->clk); - - clock->name = res->clock[i]; - - clock->nfreqs = 0; - while (res->clock_rate[i][clock->nfreqs]) - clock->nfreqs++; - - if (!clock->nfreqs) { - clock->freq = NULL; - continue; - } - - clock->freq = devm_kcalloc(dev, - clock->nfreqs, - sizeof(*clock->freq), - GFP_KERNEL); - if (!clock->freq) - return -ENOMEM; - - for (j = 0; j < clock->nfreqs; j++) - clock->freq[j] = res->clock_rate[i][j]; - } - - mutex_init(&vfe->power_lock); - vfe->power_count = 0; - - mutex_init(&vfe->stream_lock); - vfe->stream_count = 0; - - spin_lock_init(&vfe->output_lock); - - vfe->id = 0; - vfe->reg_update = 0; - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { - vfe->line[i].video_out.type = - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vfe->line[i].video_out.camss = camss; - vfe->line[i].id = i; - init_completion(&vfe->line[i].output.sof); - init_completion(&vfe->line[i].output.reg_update); - } - - init_completion(&vfe->reset_complete); - init_completion(&vfe->halt_complete); - - return 0; -} - -/* - * msm_vfe_get_vfe_id - Get VFE HW module id - * @entity: Pointer to VFE media entity structure - * @id: Return CSID HW module id here - */ -void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id) -{ - struct v4l2_subdev *sd; - struct vfe_line *line; - struct vfe_device *vfe; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - vfe = to_vfe(line); - - *id = vfe->id; -} - -/* - * msm_vfe_get_vfe_line_id - Get VFE line id by media entity - * @entity: Pointer to VFE media entity structure - * @id: Return VFE line id here - */ -void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id) -{ - struct v4l2_subdev *sd; - struct vfe_line *line; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - - *id = line->id; -} - -/* - * vfe_link_setup - Setup VFE connections - * @entity: Pointer to media entity structure - * @local: Pointer to local pad - * @remote: Pointer to remote pad - * @flags: Link flags - * - * Return 0 on success - */ -static int vfe_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) -{ - if (flags & MEDIA_LNK_FL_ENABLED) - if (media_entity_remote_pad(local)) - return -EBUSY; - - return 0; -} - -static const struct v4l2_subdev_core_ops vfe_core_ops = { - .s_power = vfe_set_power, -}; - -static const struct v4l2_subdev_video_ops vfe_video_ops = { - .s_stream = vfe_set_stream, -}; - -static const struct v4l2_subdev_pad_ops vfe_pad_ops = { - .enum_mbus_code = vfe_enum_mbus_code, - .enum_frame_size = vfe_enum_frame_size, - .get_fmt = vfe_get_format, - .set_fmt = vfe_set_format, - .get_selection = vfe_get_selection, - .set_selection = vfe_set_selection, -}; - -static const struct v4l2_subdev_ops vfe_v4l2_ops = { - .core = &vfe_core_ops, - .video = &vfe_video_ops, - .pad = &vfe_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = { - .open = vfe_init_formats, -}; - -static const struct media_entity_operations vfe_media_ops = { - .link_setup = vfe_link_setup, - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct camss_video_ops camss_vfe_video_ops = { - .queue_buffer = vfe_queue_buffer, - .flush_buffers = vfe_flush_buffers, -}; - -void msm_vfe_stop_streaming(struct vfe_device *vfe) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) - msm_video_stop_streaming(&vfe->line[i].video_out); -} - -/* - * msm_vfe_register_entities - Register subdev node for VFE module - * @vfe: VFE device - * @v4l2_dev: V4L2 device - * - * Initialize and register a subdev node for the VFE module. Then - * call msm_video_register() to register the video device node which - * will be connected to this subdev node. Then actually create the - * media link between them. - * - * Return 0 on success or a negative error code otherwise - */ -int msm_vfe_register_entities(struct vfe_device *vfe, - struct v4l2_device *v4l2_dev) -{ - struct device *dev = to_device(vfe); - struct v4l2_subdev *sd; - struct media_pad *pads; - struct camss_video *video_out; - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { - char name[32]; - - sd = &vfe->line[i].subdev; - pads = vfe->line[i].pads; - video_out = &vfe->line[i].video_out; - - v4l2_subdev_init(sd, &vfe_v4l2_ops); - sd->internal_ops = &vfe_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) - snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", - MSM_VFE_NAME, vfe->id, "pix"); - else - snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d", - MSM_VFE_NAME, vfe->id, "rdi", i); - - v4l2_set_subdevdata(sd, &vfe->line[i]); - - ret = vfe_init_formats(sd, NULL); - if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); - goto error_init; - } - - pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - - sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; - sd->entity.ops = &vfe_media_ops; - ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM, - pads); - if (ret < 0) { - dev_err(dev, "Failed to init media entity: %d\n", ret); - goto error_init; - } - - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); - goto error_reg_subdev; - } - - video_out->ops = &camss_vfe_video_ops; - video_out->bpl_alignment = 8; - video_out->line_based = 0; - if (i == VFE_LINE_PIX) { - video_out->bpl_alignment = 16; - video_out->line_based = 1; - } - snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", - MSM_VFE_NAME, vfe->id, "video", i); - ret = msm_video_register(video_out, v4l2_dev, name, - i == VFE_LINE_PIX ? 1 : 0); - if (ret < 0) { - dev_err(dev, "Failed to register video node: %d\n", - ret); - goto error_reg_video; - } - - ret = media_create_pad_link( - &sd->entity, MSM_VFE_PAD_SRC, - &video_out->vdev.entity, 0, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - if (ret < 0) { - dev_err(dev, "Failed to link %s->%s entities: %d\n", - sd->entity.name, video_out->vdev.entity.name, - ret); - goto error_link; - } - } - - return 0; - -error_link: - msm_video_unregister(video_out); - -error_reg_video: - v4l2_device_unregister_subdev(sd); - -error_reg_subdev: - media_entity_cleanup(&sd->entity); - -error_init: - for (i--; i >= 0; i--) { - sd = &vfe->line[i].subdev; - video_out = &vfe->line[i].video_out; - - msm_video_unregister(video_out); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - } - - return ret; -} - -/* - * msm_vfe_unregister_entities - Unregister VFE module subdev node - * @vfe: VFE device - */ -void msm_vfe_unregister_entities(struct vfe_device *vfe) -{ - int i; - - mutex_destroy(&vfe->power_lock); - mutex_destroy(&vfe->stream_lock); - - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { - struct v4l2_subdev *sd = &vfe->line[i].subdev; - struct camss_video *video_out = &vfe->line[i].video_out; - - msm_video_unregister(video_out); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - } -} diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h deleted file mode 100644 index 53d5b66a9dfb..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * camss-vfe.h - * - * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_VFE_H -#define QC_MSM_CAMSS_VFE_H - -#include -#include -#include -#include -#include - -#include "camss-video.h" - -#define MSM_VFE_PAD_SINK 0 -#define MSM_VFE_PAD_SRC 1 -#define MSM_VFE_PADS_NUM 2 - -#define MSM_VFE_LINE_NUM 4 -#define MSM_VFE_IMAGE_MASTERS_NUM 7 -#define MSM_VFE_COMPOSITE_IRQ_NUM 4 - -#define MSM_VFE_VFE0_UB_SIZE 1023 -#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) -#define MSM_VFE_VFE1_UB_SIZE 1535 -#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) - -enum vfe_output_state { - VFE_OUTPUT_OFF, - VFE_OUTPUT_RESERVED, - VFE_OUTPUT_SINGLE, - VFE_OUTPUT_CONTINUOUS, - VFE_OUTPUT_IDLE, - VFE_OUTPUT_STOPPING -}; - -enum vfe_line_id { - VFE_LINE_NONE = -1, - VFE_LINE_RDI0 = 0, - VFE_LINE_RDI1 = 1, - VFE_LINE_RDI2 = 2, - VFE_LINE_PIX = 3 -}; - -struct vfe_output { - u8 wm_num; - u8 wm_idx[3]; - - int active_buf; - struct camss_buffer *buf[2]; - struct camss_buffer *last_buffer; - struct list_head pending_bufs; - - unsigned int drop_update_idx; - - enum vfe_output_state state; - unsigned int sequence; - int wait_sof; - int wait_reg_update; - struct completion sof; - struct completion reg_update; -}; - -struct vfe_line { - enum vfe_line_id id; - struct v4l2_subdev subdev; - struct media_pad pads[MSM_VFE_PADS_NUM]; - struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM]; - struct v4l2_rect compose; - struct v4l2_rect crop; - struct camss_video video_out; - struct vfe_output output; -}; - -struct vfe_device { - u8 id; - void __iomem *base; - u32 irq; - char irq_name[30]; - struct camss_clock *clock; - int nclocks; - struct completion reset_complete; - struct completion halt_complete; - struct mutex power_lock; - int power_count; - struct mutex stream_lock; - int stream_count; - spinlock_t output_lock; - enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; - struct vfe_line line[MSM_VFE_LINE_NUM]; - u32 reg_update; - u8 was_streaming; -}; - -struct resources; - -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res); - -int msm_vfe_register_entities(struct vfe_device *vfe, - struct v4l2_device *v4l2_dev); - -void msm_vfe_unregister_entities(struct vfe_device *vfe); - -void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); -void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); - -void msm_vfe_stop_streaming(struct vfe_device *vfe); - -#endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c deleted file mode 100644 index ffaa2849e0c1..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-video.c +++ /dev/null @@ -1,859 +0,0 @@ -/* - * camss-video.c - * - * Qualcomm MSM Camera Subsystem - V4L2 device node - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include - -#include "camss-video.h" -#include "camss.h" - -struct fract { - u8 numerator; - u8 denominator; -}; - -/* - * struct camss_format_info - ISP media bus format information - * @code: V4L2 media bus format code - * @pixelformat: V4L2 pixel format FCC identifier - * @planes: Number of planes - * @hsub: Horizontal subsampling (for each plane) - * @vsub: Vertical subsampling (for each plane) - * @bpp: Bits per pixel when stored in memory (for each plane) - */ -struct camss_format_info { - u32 code; - u32 pixelformat; - u8 planes; - struct fract hsub[3]; - struct fract vsub[3]; - unsigned int bpp[3]; -}; - -static const struct camss_format_info formats_rdi[] = { - { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, -}; - -static const struct camss_format_info formats_pix[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, -}; - -/* ----------------------------------------------------------------------------- - * Helper functions - */ - -static int video_find_format(u32 code, u32 pixelformat, - const struct camss_format_info *formats, - unsigned int nformats) -{ - int i; - - for (i = 0; i < nformats; i++) { - if (formats[i].code == code && - formats[i].pixelformat == pixelformat) - return i; - } - - for (i = 0; i < nformats; i++) - if (formats[i].code == code) - return i; - - WARN_ON(1); - - return -EINVAL; -} - -/* - * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane - * @mbus: v4l2_mbus_framefmt format (input) - * @pix: v4l2_pix_format_mplane format (output) - * @f: a pointer to formats array element to be used for the conversion - * @alignment: bytesperline alignment value - * - * Fill the output pix structure with information from the input mbus format. - * - * Return 0 on success or a negative error code otherwise - */ -static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, - struct v4l2_pix_format_mplane *pix, - const struct camss_format_info *f, - unsigned int alignment) -{ - unsigned int i; - u32 bytesperline; - - memset(pix, 0, sizeof(*pix)); - v4l2_fill_pix_format_mplane(pix, mbus); - pix->pixelformat = f->pixelformat; - pix->num_planes = f->planes; - for (i = 0; i < pix->num_planes; i++) { - bytesperline = pix->width / f->hsub[i].numerator * - f->hsub[i].denominator * f->bpp[i] / 8; - bytesperline = ALIGN(bytesperline, alignment); - pix->plane_fmt[i].bytesperline = bytesperline; - pix->plane_fmt[i].sizeimage = pix->height / - f->vsub[i].numerator * f->vsub[i].denominator * - bytesperline; - } - - return 0; -} - -static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, - u32 *pad) -{ - struct media_pad *remote; - - remote = media_entity_remote_pad(&video->pad); - - if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) - return NULL; - - if (pad) - *pad = remote->index; - - return media_entity_to_v4l2_subdev(remote->entity); -} - -static int video_get_subdev_format(struct camss_video *video, - struct v4l2_format *format) -{ - struct v4l2_subdev_format fmt; - struct v4l2_subdev *subdev; - u32 pad; - int ret; - - subdev = video_remote_subdev(video, &pad); - if (subdev == NULL) - return -EPIPE; - - fmt.pad = pad; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); - if (ret) - return ret; - - ret = video_find_format(fmt.format.code, - format->fmt.pix_mp.pixelformat, - video->formats, video->nformats); - if (ret < 0) - return ret; - - format->type = video->type; - - return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, - &video->formats[ret], video->bpl_alignment); -} - -/* ----------------------------------------------------------------------------- - * Video queue operations - */ - -static int video_queue_setup(struct vb2_queue *q, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct camss_video *video = vb2_get_drv_priv(q); - const struct v4l2_pix_format_mplane *format = - &video->active_fmt.fmt.pix_mp; - unsigned int i; - - if (*num_planes) { - if (*num_planes != format->num_planes) - return -EINVAL; - - for (i = 0; i < *num_planes; i++) - if (sizes[i] < format->plane_fmt[i].sizeimage) - return -EINVAL; - - return 0; - } - - *num_planes = format->num_planes; - - for (i = 0; i < *num_planes; i++) - sizes[i] = format->plane_fmt[i].sizeimage; - - return 0; -} - -static int video_buf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, - vb); - const struct v4l2_pix_format_mplane *format = - &video->active_fmt.fmt.pix_mp; - struct sg_table *sgt; - unsigned int i; - - for (i = 0; i < format->num_planes; i++) { - sgt = vb2_dma_sg_plane_desc(vb, i); - if (!sgt) - return -EFAULT; - - buffer->addr[i] = sg_dma_address(sgt->sgl); - } - - if (format->pixelformat == V4L2_PIX_FMT_NV12 || - format->pixelformat == V4L2_PIX_FMT_NV21 || - format->pixelformat == V4L2_PIX_FMT_NV16 || - format->pixelformat == V4L2_PIX_FMT_NV61) - buffer->addr[1] = buffer->addr[0] + - format->plane_fmt[0].bytesperline * - format->height; - - return 0; -} - -static int video_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); - const struct v4l2_pix_format_mplane *format = - &video->active_fmt.fmt.pix_mp; - unsigned int i; - - for (i = 0; i < format->num_planes; i++) { - if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i)) - return -EINVAL; - - vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage); - } - - vbuf->field = V4L2_FIELD_NONE; - - return 0; -} - -static void video_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, - vb); - - video->ops->queue_buffer(video, buffer); -} - -static int video_check_format(struct camss_video *video) -{ - struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp; - struct v4l2_format format; - struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp; - int ret; - - sd_pix->pixelformat = pix->pixelformat; - ret = video_get_subdev_format(video, &format); - if (ret < 0) - return ret; - - if (pix->pixelformat != sd_pix->pixelformat || - pix->height != sd_pix->height || - pix->width != sd_pix->width || - pix->num_planes != sd_pix->num_planes || - pix->field != format.fmt.pix_mp.field) - return -EPIPE; - - return 0; -} - -static int video_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct camss_video *video = vb2_get_drv_priv(q); - struct video_device *vdev = &video->vdev; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - int ret; - - ret = media_pipeline_start(&vdev->entity, &video->pipe); - if (ret < 0) - return ret; - - ret = video_check_format(video); - if (ret < 0) - goto error; - - entity = &vdev->entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - pad = media_entity_remote_pad(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - ret = v4l2_subdev_call(subdev, video, s_stream, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) - goto error; - } - - return 0; - -error: - media_pipeline_stop(&vdev->entity); - - video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); - - return ret; -} - -static void video_stop_streaming(struct vb2_queue *q) -{ - struct camss_video *video = vb2_get_drv_priv(q); - struct video_device *vdev = &video->vdev; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - - entity = &vdev->entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - pad = media_entity_remote_pad(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - v4l2_subdev_call(subdev, video, s_stream, 0); - } - - media_pipeline_stop(&vdev->entity); - - video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); -} - -static const struct vb2_ops msm_video_vb2_q_ops = { - .queue_setup = video_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .buf_init = video_buf_init, - .buf_prepare = video_buf_prepare, - .buf_queue = video_buf_queue, - .start_streaming = video_start_streaming, - .stop_streaming = video_stop_streaming, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 ioctls - */ - -static int video_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct camss_video *video = video_drvdata(file); - - strlcpy(cap->driver, "qcom-camss", sizeof(cap->driver)); - strlcpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(video->camss->dev)); - - return 0; -} - -static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) -{ - struct camss_video *video = video_drvdata(file); - int i, j, k; - - if (f->type != video->type) - return -EINVAL; - - if (f->index >= video->nformats) - return -EINVAL; - - /* find index "i" of "k"th unique pixelformat in formats array */ - k = -1; - for (i = 0; i < video->nformats; i++) { - for (j = 0; j < i; j++) { - if (video->formats[i].pixelformat == - video->formats[j].pixelformat) - break; - } - - if (j == i) - k++; - - if (k == f->index) - break; - } - - if (k < f->index) - return -EINVAL; - - f->pixelformat = video->formats[i].pixelformat; - - return 0; -} - -static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct camss_video *video = video_drvdata(file); - - *f = video->active_fmt; - - return 0; -} - -static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp; - const struct camss_format_info *fi; - struct v4l2_plane_pix_format *p; - u32 bytesperline[3] = { 0 }; - u32 sizeimage[3] = { 0 }; - u32 width, height; - u32 bpl, lines; - int i, j; - - pix_mp = &f->fmt.pix_mp; - - if (video->line_based) - for (i = 0; i < pix_mp->num_planes && i < 3; i++) { - p = &pix_mp->plane_fmt[i]; - bytesperline[i] = clamp_t(u32, p->bytesperline, - 1, 65528); - sizeimage[i] = clamp_t(u32, p->sizeimage, - bytesperline[i], - bytesperline[i] * 4096); - } - - for (j = 0; j < video->nformats; j++) - if (pix_mp->pixelformat == video->formats[j].pixelformat) - break; - - if (j == video->nformats) - j = 0; /* default format */ - - fi = &video->formats[j]; - width = pix_mp->width; - height = pix_mp->height; - - memset(pix_mp, 0, sizeof(*pix_mp)); - - pix_mp->pixelformat = fi->pixelformat; - pix_mp->width = clamp_t(u32, width, 1, 8191); - pix_mp->height = clamp_t(u32, height, 1, 8191); - pix_mp->num_planes = fi->planes; - for (i = 0; i < pix_mp->num_planes; i++) { - bpl = pix_mp->width / fi->hsub[i].numerator * - fi->hsub[i].denominator * fi->bpp[i] / 8; - bpl = ALIGN(bpl, video->bpl_alignment); - pix_mp->plane_fmt[i].bytesperline = bpl; - pix_mp->plane_fmt[i].sizeimage = pix_mp->height / - fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; - } - - pix_mp->field = V4L2_FIELD_NONE; - pix_mp->colorspace = V4L2_COLORSPACE_SRGB; - pix_mp->flags = 0; - pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace); - pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - pix_mp->colorspace, pix_mp->ycbcr_enc); - pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace); - - if (video->line_based) - for (i = 0; i < pix_mp->num_planes; i++) { - p = &pix_mp->plane_fmt[i]; - p->bytesperline = clamp_t(u32, p->bytesperline, - 1, 65528); - p->sizeimage = clamp_t(u32, p->sizeimage, - p->bytesperline, - p->bytesperline * 4096); - lines = p->sizeimage / p->bytesperline; - - if (p->bytesperline < bytesperline[i]) - p->bytesperline = ALIGN(bytesperline[i], 8); - - if (p->sizeimage < p->bytesperline * lines) - p->sizeimage = p->bytesperline * lines; - - if (p->sizeimage < sizeimage[i]) - p->sizeimage = sizeimage[i]; - } - - return 0; -} - -static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct camss_video *video = video_drvdata(file); - - return __video_try_fmt(video, f); -} - -static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct camss_video *video = video_drvdata(file); - int ret; - - if (vb2_is_busy(&video->vb2_q)) - return -EBUSY; - - ret = __video_try_fmt(video, f); - if (ret < 0) - return ret; - - video->active_fmt = *f; - - return 0; -} - -static int video_enum_input(struct file *file, void *fh, - struct v4l2_input *input) -{ - if (input->index > 0) - return -EINVAL; - - strlcpy(input->name, "camera", sizeof(input->name)); - input->type = V4L2_INPUT_TYPE_CAMERA; - - return 0; -} - -static int video_g_input(struct file *file, void *fh, unsigned int *input) -{ - *input = 0; - - return 0; -} - -static int video_s_input(struct file *file, void *fh, unsigned int input) -{ - return input == 0 ? 0 : -EINVAL; -} - -static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { - .vidioc_querycap = video_querycap, - .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt, - .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, - .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, - .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_enum_input = video_enum_input, - .vidioc_g_input = video_g_input, - .vidioc_s_input = video_s_input, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 file operations - */ - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct camss_video *video = video_drvdata(file); - struct v4l2_fh *vfh; - int ret; - - mutex_lock(&video->lock); - - vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); - if (vfh == NULL) { - ret = -ENOMEM; - goto error_alloc; - } - - v4l2_fh_init(vfh, vdev); - v4l2_fh_add(vfh); - - file->private_data = vfh; - - ret = v4l2_pipeline_pm_use(&vdev->entity, 1); - if (ret < 0) { - dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", - ret); - goto error_pm_use; - } - - mutex_unlock(&video->lock); - - return 0; - -error_pm_use: - v4l2_fh_release(file); - -error_alloc: - mutex_unlock(&video->lock); - - return ret; -} - -static int video_release(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - - vb2_fop_release(file); - - v4l2_pipeline_pm_use(&vdev->entity, 0); - - file->private_data = NULL; - - return 0; -} - -static const struct v4l2_file_operations msm_vid_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, - .open = video_open, - .release = video_release, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .read = vb2_fop_read, -}; - -/* ----------------------------------------------------------------------------- - * CAMSS video core - */ - -static void msm_video_release(struct video_device *vdev) -{ - struct camss_video *video = video_get_drvdata(vdev); - - media_entity_cleanup(&vdev->entity); - - mutex_destroy(&video->q_lock); - mutex_destroy(&video->lock); - - if (atomic_dec_and_test(&video->camss->ref_count)) - camss_delete(video->camss); -} - -/* - * msm_video_init_format - Helper function to initialize format - * @video: struct camss_video - * - * Initialize pad format with default value. - * - * Return 0 on success or a negative error code otherwise - */ -static int msm_video_init_format(struct camss_video *video) -{ - int ret; - struct v4l2_format format = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - .fmt.pix_mp = { - .width = 1920, - .height = 1080, - .pixelformat = video->formats[0].pixelformat, - }, - }; - - ret = __video_try_fmt(video, &format); - if (ret < 0) - return ret; - - video->active_fmt = format; - - return 0; -} - -/* - * msm_video_register - Register a video device node - * @video: struct camss_video - * @v4l2_dev: V4L2 device - * @name: name to be used for the video device node - * - * Initialize and register a video device node to a V4L2 device. Also - * initialize the vb2 queue. - * - * Return 0 on success or a negative error code otherwise - */ - -int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix) -{ - struct media_pad *pad = &video->pad; - struct video_device *vdev; - struct vb2_queue *q; - int ret; - - vdev = &video->vdev; - - mutex_init(&video->q_lock); - - q = &video->vb2_q; - q->drv_priv = video; - q->mem_ops = &vb2_dma_sg_memops; - q->ops = &msm_video_vb2_q_ops; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->buf_struct_size = sizeof(struct camss_buffer); - q->dev = video->camss->dev; - q->lock = &video->q_lock; - ret = vb2_queue_init(q); - if (ret < 0) { - dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); - goto error_vb2_init; - } - - pad->flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vdev->entity, 1, pad); - if (ret < 0) { - dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", - ret); - goto error_media_init; - } - - mutex_init(&video->lock); - - video->formats = formats_rdi; - video->nformats = ARRAY_SIZE(formats_rdi); - if (is_pix) { - video->formats = formats_pix; - video->nformats = ARRAY_SIZE(formats_pix); - } - - ret = msm_video_init_format(video); - if (ret < 0) { - dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); - goto error_video_register; - } - - vdev->fops = &msm_vid_fops; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - vdev->ioctl_ops = &msm_vid_ioctl_ops; - vdev->release = msm_video_release; - vdev->v4l2_dev = v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = &video->vb2_q; - vdev->lock = &video->lock; - strlcpy(vdev->name, name, sizeof(vdev->name)); - - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (ret < 0) { - dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", - ret); - goto error_video_register; - } - - video_set_drvdata(vdev, video); - atomic_inc(&video->camss->ref_count); - - return 0; - -error_video_register: - media_entity_cleanup(&vdev->entity); - mutex_destroy(&video->lock); -error_media_init: - vb2_queue_release(&video->vb2_q); -error_vb2_init: - mutex_destroy(&video->q_lock); - - return ret; -} - -void msm_video_stop_streaming(struct camss_video *video) -{ - if (vb2_is_streaming(&video->vb2_q)) - vb2_queue_release(&video->vb2_q); -} - -void msm_video_unregister(struct camss_video *video) -{ - atomic_inc(&video->camss->ref_count); - video_unregister_device(&video->vdev); - atomic_dec(&video->camss->ref_count); -} diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss-8x16/camss-video.h deleted file mode 100644 index 38bd1f2eec54..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-video.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * camss-video.h - * - * Qualcomm MSM Camera Subsystem - V4L2 device node - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_VIDEO_H -#define QC_MSM_CAMSS_VIDEO_H - -#include -#include -#include -#include -#include -#include -#include -#include - -struct camss_buffer { - struct vb2_v4l2_buffer vb; - dma_addr_t addr[3]; - struct list_head queue; -}; - -struct camss_video; - -struct camss_video_ops { - int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf); - int (*flush_buffers)(struct camss_video *vid, - enum vb2_buffer_state state); -}; - -struct camss_format_info; - -struct camss_video { - struct camss *camss; - struct vb2_queue vb2_q; - struct video_device vdev; - struct media_pad pad; - struct v4l2_format active_fmt; - enum v4l2_buf_type type; - struct media_pipeline pipe; - const struct camss_video_ops *ops; - struct mutex lock; - struct mutex q_lock; - unsigned int bpl_alignment; - unsigned int line_based; - const struct camss_format_info *formats; - unsigned int nformats; -}; - -void msm_video_stop_streaming(struct camss_video *video); - -int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix); - -void msm_video_unregister(struct camss_video *video); - -#endif /* QC_MSM_CAMSS_VIDEO_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c deleted file mode 100644 index 23fda6207a23..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - * camss.c - * - * Qualcomm MSM Camera Subsystem - Core - * - * Copyright (c) 2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "camss.h" - -#define CAMSS_CLOCK_MARGIN_NUMERATOR 105 -#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 - -static const struct resources csiphy_res[] = { - /* CSIPHY0 */ - { - .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy0_timer" }, - .clock_rate = { { 0 }, - { 0 }, - { 0 }, - { 100000000, 200000000 } }, - .reg = { "csiphy0", "csiphy0_clk_mux" }, - .interrupt = { "csiphy0" } - }, - - /* CSIPHY1 */ - { - .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy1_timer" }, - .clock_rate = { { 0 }, - { 0 }, - { 0 }, - { 100000000, 200000000 } }, - .reg = { "csiphy1", "csiphy1_clk_mux" }, - .interrupt = { "csiphy1" } - } -}; - -static const struct resources csid_res[] = { - /* CSID0 */ - { - .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi0_ahb", "camss_ahb", - "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, - .clock_rate = { { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 100000000, 200000000 }, - { 0 }, - { 0 }, - { 0 } }, - .reg = { "csid0" }, - .interrupt = { "csid0" } - }, - - /* CSID1 */ - { - .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi1_ahb", "camss_ahb", - "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, - .clock_rate = { { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 100000000, 200000000 }, - { 0 }, - { 0 }, - { 0 } }, - .reg = { "csid1" }, - .interrupt = { "csid1" } - }, -}; - -static const struct resources_ispif ispif_res = { - /* ISPIF */ - .clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb", - "csi0", "csi0_pix", "csi0_rdi", - "csi1", "csi1_pix", "csi1_rdi" }, - .clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" }, - .reg = { "ispif", "csi_clk_mux" }, - .interrupt = "ispif" - -}; - -static const struct resources vfe_res = { - /* VFE0 */ - .regulator = { NULL }, - .clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe", - "iface", "bus", "camss_ahb" }, - .clock_rate = { { 0 }, - { 50000000, 80000000, 100000000, 160000000, - 177780000, 200000000, 266670000, 320000000, - 400000000, 465000000 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 } }, - .reg = { "vfe0" }, - .interrupt = { "vfe0" } -}; - -/* - * camss_add_clock_margin - Add margin to clock frequency rate - * @rate: Clock frequency rate - * - * When making calculations with physical clock frequency values - * some safety margin must be added. Add it. - */ -inline void camss_add_clock_margin(u64 *rate) -{ - *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; - *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); -} - -/* - * camss_enable_clocks - Enable multiple clocks - * @nclocks: Number of clocks in clock array - * @clock: Clock array - * @dev: Device - * - * Return 0 on success or a negative error code otherwise - */ -int camss_enable_clocks(int nclocks, struct camss_clock *clock, - struct device *dev) -{ - int ret; - int i; - - for (i = 0; i < nclocks; i++) { - ret = clk_prepare_enable(clock[i].clk); - if (ret) { - dev_err(dev, "clock enable failed: %d\n", ret); - goto error; - } - } - - return 0; - -error: - for (i--; i >= 0; i--) - clk_disable_unprepare(clock[i].clk); - - return ret; -} - -/* - * camss_disable_clocks - Disable multiple clocks - * @nclocks: Number of clocks in clock array - * @clock: Clock array - */ -void camss_disable_clocks(int nclocks, struct camss_clock *clock) -{ - int i; - - for (i = nclocks - 1; i >= 0; i--) - clk_disable_unprepare(clock[i].clk); -} - -/* - * camss_find_sensor - Find a linked media entity which represents a sensor - * @entity: Media entity to start searching from - * - * Return a pointer to sensor media entity or NULL if not found - */ -static struct media_entity *camss_find_sensor(struct media_entity *entity) -{ - struct media_pad *pad; - - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - return NULL; - - pad = media_entity_remote_pad(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return NULL; - - entity = pad->entity; - - if (entity->function == MEDIA_ENT_F_CAM_SENSOR) - return entity; - } -} - -/* - * camss_get_pixel_clock - Get pixel clock rate from sensor - * @entity: Media entity in the current pipeline - * @pixel_clock: Received pixel clock value - * - * Return 0 on success or a negative error code otherwise - */ -int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) -{ - struct media_entity *sensor; - struct v4l2_subdev *subdev; - struct v4l2_ctrl *ctrl; - - sensor = camss_find_sensor(entity); - if (!sensor) - return -ENODEV; - - subdev = media_entity_to_v4l2_subdev(sensor); - - ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); - - if (!ctrl) - return -EINVAL; - - *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); - - return 0; -} - -/* - * camss_of_parse_endpoint_node - Parse port endpoint node - * @dev: Device - * @node: Device node to be parsed - * @csd: Parsed data from port endpoint node - * - * Return 0 on success or a negative error code on failure - */ -static int camss_of_parse_endpoint_node(struct device *dev, - struct device_node *node, - struct camss_async_subdev *csd) -{ - struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; - struct v4l2_fwnode_endpoint vep = { { 0 } }; - unsigned int i; - - v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); - - csd->interface.csiphy_id = vep.base.port; - - mipi_csi2 = &vep.bus.mipi_csi2; - lncfg->clk.pos = mipi_csi2->clock_lane; - lncfg->clk.pol = mipi_csi2->lane_polarities[0]; - lncfg->num_data = mipi_csi2->num_data_lanes; - - lncfg->data = devm_kcalloc(dev, - lncfg->num_data, sizeof(*lncfg->data), - GFP_KERNEL); - if (!lncfg->data) - return -ENOMEM; - - for (i = 0; i < lncfg->num_data; i++) { - lncfg->data[i].pos = mipi_csi2->data_lanes[i]; - lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; - } - - return 0; -} - -/* - * camss_of_parse_ports - Parse ports node - * @dev: Device - * @notifier: v4l2_device notifier data - * - * Return number of "port" nodes found in "ports" node - */ -static int camss_of_parse_ports(struct device *dev, - struct v4l2_async_notifier *notifier) -{ - struct device_node *node = NULL; - struct device_node *remote = NULL; - unsigned int size, i; - int ret; - - while ((node = of_graph_get_next_endpoint(dev->of_node, node))) - if (of_device_is_available(node)) - notifier->num_subdevs++; - - size = sizeof(*notifier->subdevs) * notifier->num_subdevs; - notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL); - if (!notifier->subdevs) { - dev_err(dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - i = 0; - while ((node = of_graph_get_next_endpoint(dev->of_node, node))) { - struct camss_async_subdev *csd; - - if (!of_device_is_available(node)) - continue; - - csd = devm_kzalloc(dev, sizeof(*csd), GFP_KERNEL); - if (!csd) { - of_node_put(node); - dev_err(dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - notifier->subdevs[i++] = &csd->asd; - - ret = camss_of_parse_endpoint_node(dev, node, csd); - if (ret < 0) { - of_node_put(node); - return ret; - } - - remote = of_graph_get_remote_port_parent(node); - of_node_put(node); - - if (!remote) { - dev_err(dev, "Cannot get remote parent\n"); - return -EINVAL; - } - - csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - csd->asd.match.fwnode = of_fwnode_handle(remote); - } - - return notifier->num_subdevs; -} - -/* - * camss_init_subdevices - Initialize subdev structures and resources - * @camss: CAMSS device - * - * Return 0 on success or a negative error code on failure - */ -static int camss_init_subdevices(struct camss *camss) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - ret = msm_csiphy_subdev_init(&camss->csiphy[i], - &csiphy_res[i], i); - if (ret < 0) { - dev_err(camss->dev, - "Failed to init csiphy%d sub-device: %d\n", - i, ret); - return ret; - } - } - - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - ret = msm_csid_subdev_init(&camss->csid[i], - &csid_res[i], i); - if (ret < 0) { - dev_err(camss->dev, - "Failed to init csid%d sub-device: %d\n", - i, ret); - return ret; - } - } - - ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res); - if (ret < 0) { - dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", - ret); - return ret; - } - - ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res); - if (ret < 0) { - dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret); - return ret; - } - - return 0; -} - -/* - * camss_register_entities - Register subdev nodes and create links - * @camss: CAMSS device - * - * Return 0 on success or a negative error code on failure - */ -static int camss_register_entities(struct camss *camss) -{ - int i, j; - int ret; - - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - ret = msm_csiphy_register_entity(&camss->csiphy[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csiphy%d entity: %d\n", - i, ret); - goto err_reg_csiphy; - } - } - - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - ret = msm_csid_register_entity(&camss->csid[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csid%d entity: %d\n", - i, ret); - goto err_reg_csid; - } - } - - ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register ispif entities: %d\n", - ret); - goto err_reg_ispif; - } - - ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register vfe entities: %d\n", - ret); - goto err_reg_vfe; - } - - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - for (j = 0; j < ARRAY_SIZE(camss->csid); j++) { - ret = media_create_pad_link( - &camss->csiphy[i].subdev.entity, - MSM_CSIPHY_PAD_SRC, - &camss->csid[j].subdev.entity, - MSM_CSID_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->csiphy[i].subdev.entity.name, - camss->csid[j].subdev.entity.name, - ret); - goto err_link; - } - } - } - - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) { - ret = media_create_pad_link( - &camss->csid[i].subdev.entity, - MSM_CSID_PAD_SRC, - &camss->ispif.line[j].subdev.entity, - MSM_ISPIF_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->csid[i].subdev.entity.name, - camss->ispif.line[j].subdev.entity.name, - ret); - goto err_link; - } - } - } - - for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) { - for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) { - ret = media_create_pad_link( - &camss->ispif.line[i].subdev.entity, - MSM_ISPIF_PAD_SRC, - &camss->vfe.line[j].subdev.entity, - MSM_VFE_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->ispif.line[i].subdev.entity.name, - camss->vfe.line[j].subdev.entity.name, - ret); - goto err_link; - } - } - } - - return 0; - -err_link: - msm_vfe_unregister_entities(&camss->vfe); -err_reg_vfe: - msm_ispif_unregister_entities(&camss->ispif); -err_reg_ispif: - - i = ARRAY_SIZE(camss->csid); -err_reg_csid: - for (i--; i >= 0; i--) - msm_csid_unregister_entity(&camss->csid[i]); - - i = ARRAY_SIZE(camss->csiphy); -err_reg_csiphy: - for (i--; i >= 0; i--) - msm_csiphy_unregister_entity(&camss->csiphy[i]); - - return ret; -} - -/* - * camss_unregister_entities - Unregister subdev nodes - * @camss: CAMSS device - * - * Return 0 on success or a negative error code on failure - */ -static void camss_unregister_entities(struct camss *camss) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) - msm_csiphy_unregister_entity(&camss->csiphy[i]); - - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) - msm_csid_unregister_entity(&camss->csid[i]); - - msm_ispif_unregister_entities(&camss->ispif); - msm_vfe_unregister_entities(&camss->vfe); -} - -static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) -{ - struct camss *camss = container_of(async, struct camss, notifier); - struct camss_async_subdev *csd = - container_of(asd, struct camss_async_subdev, asd); - u8 id = csd->interface.csiphy_id; - struct csiphy_device *csiphy = &camss->csiphy[id]; - - csiphy->cfg.csi2 = &csd->interface.csi2; - subdev->host_priv = csiphy; - - return 0; -} - -static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) -{ - struct camss *camss = container_of(async, struct camss, notifier); - struct v4l2_device *v4l2_dev = &camss->v4l2_dev; - struct v4l2_subdev *sd; - int ret; - - list_for_each_entry(sd, &v4l2_dev->subdevs, list) { - if (sd->host_priv) { - struct media_entity *sensor = &sd->entity; - struct csiphy_device *csiphy = - (struct csiphy_device *) sd->host_priv; - struct media_entity *input = &csiphy->subdev.entity; - unsigned int i; - - for (i = 0; i < sensor->num_pads; i++) { - if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) - break; - } - if (i == sensor->num_pads) { - dev_err(camss->dev, - "No source pad in external entity\n"); - return -EINVAL; - } - - ret = media_create_pad_link(sensor, i, - input, MSM_CSIPHY_PAD_SINK, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - sensor->name, input->name, ret); - return ret; - } - } - } - - ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); - if (ret < 0) - return ret; - - return media_device_register(&camss->media_dev); -} - -static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { - .bound = camss_subdev_notifier_bound, - .complete = camss_subdev_notifier_complete, -}; - -static const struct media_device_ops camss_media_ops = { - .link_notify = v4l2_pipeline_link_notify, -}; - -/* - * camss_probe - Probe CAMSS platform device - * @pdev: Pointer to CAMSS platform device - * - * Return 0 on success or a negative error code on failure - */ -static int camss_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct camss *camss; - int ret; - - camss = kzalloc(sizeof(*camss), GFP_KERNEL); - if (!camss) - return -ENOMEM; - - atomic_set(&camss->ref_count, 0); - camss->dev = dev; - platform_set_drvdata(pdev, camss); - - ret = camss_of_parse_ports(dev, &camss->notifier); - if (ret < 0) - return ret; - - ret = camss_init_subdevices(camss); - if (ret < 0) - return ret; - - ret = dma_set_mask_and_coherent(dev, 0xffffffff); - if (ret) - return ret; - - camss->media_dev.dev = camss->dev; - strlcpy(camss->media_dev.model, "Qualcomm Camera Subsystem", - sizeof(camss->media_dev.model)); - camss->media_dev.ops = &camss_media_ops; - media_device_init(&camss->media_dev); - - camss->v4l2_dev.mdev = &camss->media_dev; - ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); - if (ret < 0) { - dev_err(dev, "Failed to register V4L2 device: %d\n", ret); - return ret; - } - - ret = camss_register_entities(camss); - if (ret < 0) - goto err_register_entities; - - if (camss->notifier.num_subdevs) { - camss->notifier.ops = &camss_subdev_notifier_ops; - - ret = v4l2_async_notifier_register(&camss->v4l2_dev, - &camss->notifier); - if (ret) { - dev_err(dev, - "Failed to register async subdev nodes: %d\n", - ret); - goto err_register_subdevs; - } - } else { - ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); - if (ret < 0) { - dev_err(dev, "Failed to register subdev nodes: %d\n", - ret); - goto err_register_subdevs; - } - - ret = media_device_register(&camss->media_dev); - if (ret < 0) { - dev_err(dev, "Failed to register media device: %d\n", - ret); - goto err_register_subdevs; - } - } - - return 0; - -err_register_subdevs: - camss_unregister_entities(camss); -err_register_entities: - v4l2_device_unregister(&camss->v4l2_dev); - - return ret; -} - -void camss_delete(struct camss *camss) -{ - v4l2_device_unregister(&camss->v4l2_dev); - media_device_unregister(&camss->media_dev); - media_device_cleanup(&camss->media_dev); - - kfree(camss); -} - -/* - * camss_remove - Remove CAMSS platform device - * @pdev: Pointer to CAMSS platform device - * - * Always returns 0. - */ -static int camss_remove(struct platform_device *pdev) -{ - struct camss *camss = platform_get_drvdata(pdev); - - msm_vfe_stop_streaming(&camss->vfe); - - v4l2_async_notifier_unregister(&camss->notifier); - camss_unregister_entities(camss); - - if (atomic_read(&camss->ref_count) == 0) - camss_delete(camss); - - return 0; -} - -static const struct of_device_id camss_dt_match[] = { - { .compatible = "qcom,msm8916-camss" }, - { } -}; - -MODULE_DEVICE_TABLE(of, camss_dt_match); - -static struct platform_driver qcom_camss_driver = { - .probe = camss_probe, - .remove = camss_remove, - .driver = { - .name = "qcom-camss", - .of_match_table = camss_dt_match, - }, -}; - -module_platform_driver(qcom_camss_driver); - -MODULE_ALIAS("platform:qcom-camss"); -MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); -MODULE_AUTHOR("Todor Tomov "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/camss-8x16/camss.h b/drivers/media/platform/qcom/camss-8x16/camss.h deleted file mode 100644 index 4ad223443e4b..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * camss.h - * - * Qualcomm MSM Camera Subsystem - Core - * - * Copyright (c) 2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_H -#define QC_MSM_CAMSS_H - -#include -#include -#include -#include -#include -#include -#include - -#include "camss-csid.h" -#include "camss-csiphy.h" -#include "camss-ispif.h" -#include "camss-vfe.h" - -#define CAMSS_CSID_NUM 2 -#define CAMSS_CSIPHY_NUM 2 - -#define to_camss(ptr_module) \ - container_of(ptr_module, struct camss, ptr_module) - -#define to_device(ptr_module) \ - (to_camss(ptr_module)->dev) - -#define module_pointer(ptr_module, index) \ - ((const struct ptr_module##_device (*)[]) &(ptr_module[-(index)])) - -#define to_camss_index(ptr_module, index) \ - container_of(module_pointer(ptr_module, index), \ - struct camss, ptr_module) - -#define to_device_index(ptr_module, index) \ - (to_camss_index(ptr_module, index)->dev) - -#define CAMSS_RES_MAX 15 - -struct resources { - char *regulator[CAMSS_RES_MAX]; - char *clock[CAMSS_RES_MAX]; - u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; - char *reg[CAMSS_RES_MAX]; - char *interrupt[CAMSS_RES_MAX]; -}; - -struct resources_ispif { - char *clock[CAMSS_RES_MAX]; - char *clock_for_reset[CAMSS_RES_MAX]; - char *reg[CAMSS_RES_MAX]; - char *interrupt; -}; - -struct camss { - struct v4l2_device v4l2_dev; - struct v4l2_async_notifier notifier; - struct media_device media_dev; - struct device *dev; - struct csiphy_device csiphy[CAMSS_CSIPHY_NUM]; - struct csid_device csid[CAMSS_CSID_NUM]; - struct ispif_device ispif; - struct vfe_device vfe; - atomic_t ref_count; -}; - -struct camss_camera_interface { - u8 csiphy_id; - struct csiphy_csi2_cfg csi2; -}; - -struct camss_async_subdev { - struct camss_camera_interface interface; - struct v4l2_async_subdev asd; -}; - -struct camss_clock { - struct clk *clk; - const char *name; - u32 *freq; - u32 nfreqs; -}; - -void camss_add_clock_margin(u64 *rate); -int camss_enable_clocks(int nclocks, struct camss_clock *clock, - struct device *dev); -void camss_disable_clocks(int nclocks, struct camss_clock *clock); -int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock); -void camss_delete(struct camss *camss); - -#endif /* QC_MSM_CAMSS_H */ diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile new file mode 100644 index 000000000000..3c4024fbb768 --- /dev/null +++ b/drivers/media/platform/qcom/camss/Makefile @@ -0,0 +1,11 @@ +# Makefile for Qualcomm CAMSS driver + +qcom-camss-objs += \ + camss.o \ + camss-csid.o \ + camss-csiphy.o \ + camss-ispif.o \ + camss-vfe.o \ + camss-video.o \ + +obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c new file mode 100644 index 000000000000..39ea27b9da3b --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -0,0 +1,1094 @@ +/* + * camss-csid.c + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-csid.h" +#include "camss.h" + +#define MSM_CSID_NAME "msm_csid" + +#define CAMSS_CSID_HW_VERSION 0x0 +#define CAMSS_CSID_CORE_CTRL_0 0x004 +#define CAMSS_CSID_CORE_CTRL_1 0x008 +#define CAMSS_CSID_RST_CMD 0x00c +#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n)) +#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060 +#define CAMSS_CSID_IRQ_MASK 0x064 +#define CAMSS_CSID_IRQ_STATUS 0x068 +#define CAMSS_CSID_TG_CTRL 0x0a0 +#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 +#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 +#define CAMSS_CSID_TG_VC_CFG 0x0a4 +#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff +#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f +#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) + +#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 +#define DATA_TYPE_YUV422_8BIT 0x1e +#define DATA_TYPE_RAW_6BIT 0x28 +#define DATA_TYPE_RAW_8BIT 0x2a +#define DATA_TYPE_RAW_10BIT 0x2b +#define DATA_TYPE_RAW_12BIT 0x2c + +#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 +#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 +#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 +#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 + +#define CSID_RESET_TIMEOUT_MS 500 + +struct csid_fmts { + u32 code; + u8 data_type; + u8 decode_format; + u8 bpp; + u8 spp; /* bus samples per pixel */ +}; + +static const struct csid_fmts csid_input_fmts[] = { + { + MEDIA_BUS_FMT_UYVY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + } +}; + +static const struct csid_fmts *csid_get_fmt_entry(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) + if (code == csid_input_fmts[i].code) + return &csid_input_fmts[i]; + + WARN(1, "Unknown format\n"); + + return &csid_input_fmts[0]; +} + +/* + * csid_isr - CSID module interrupt handler + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 value; + + value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); + writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); + + if ((value >> 11) & 0x1) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +/* + * csid_set_clock_rates - Calculate and set clock rates on CSID module + * @csiphy: CSID device + */ +static int csid_set_clock_rates(struct csid_device *csid) +{ + struct device *dev = to_device_index(csid, csid->id); + u32 pixel_clock; + int i, j; + int ret; + + ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock); + if (ret) + pixel_clock = 0; + + for (i = 0; i < csid->nclocks; i++) { + struct camss_clock *clock = &csid->clock[i]; + + if (!strcmp(clock->name, "csi0") || + !strcmp(clock->name, "csi1")) { + u8 bpp = csid_get_fmt_entry( + csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; + u8 num_lanes = csid->phy.lane_cnt; + u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); + long rate; + + camss_add_clock_margin(&min_rate); + + for (j = 0; j < clock->nfreqs; j++) + if (min_rate < clock->freq[j]) + break; + + if (j == clock->nfreqs) { + dev_err(dev, + "Pixel clock is too high for CSID\n"); + return -EINVAL; + } + + /* if sensor pixel clock is not available */ + /* set highest possible CSID clock rate */ + if (min_rate == 0) + j = clock->nfreqs - 1; + + rate = clk_round_rate(clock->clk, clock->freq[j]); + if (rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + rate); + return -EINVAL; + } + + ret = clk_set_rate(clock->clk, rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +/* + * csid_reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + + reinit_completion(&csid->reset_complete); + + writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(to_device_index(csid, csid->id), + "CSID reset timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * csid_set_power - Power on/off CSID module + * @sd: CSID V4L2 subdevice + * @on: Requested power state + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_set_power(struct v4l2_subdev *sd, int on) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct device *dev = to_device_index(csid, csid->id); + int ret; + + if (on) { + u32 hw_version; + + ret = regulator_enable(csid->vdda); + if (ret < 0) + return ret; + + ret = csid_set_clock_rates(csid); + if (ret < 0) { + regulator_disable(csid->vdda); + return ret; + } + + ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); + if (ret < 0) { + regulator_disable(csid->vdda); + return ret; + } + + enable_irq(csid->irq); + + ret = csid_reset(csid); + if (ret < 0) { + disable_irq(csid->irq); + camss_disable_clocks(csid->nclocks, csid->clock); + regulator_disable(csid->vdda); + return ret; + } + + hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); + dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version); + } else { + disable_irq(csid->irq); + camss_disable_clocks(csid->nclocks, csid->clock); + ret = regulator_disable(csid->vdda); + } + + return ret; +} + +/* + * csid_set_stream - Enable/disable streaming on CSID module + * @sd: CSID V4L2 subdevice + * @enable: Requested streaming state + * + * Main configuration of CSID module is also done here. + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct csid_testgen_config *tg = &csid->testgen; + u32 val; + + if (enable) { + u8 vc = 0; /* Virtual Channel 0 */ + u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ + u8 dt, dt_shift, df; + int ret; + + ret = v4l2_ctrl_handler_setup(&csid->ctrls); + if (ret < 0) { + dev_err(to_device_index(csid, csid->id), + "could not sync v4l2 controls: %d\n", ret); + return ret; + } + + if (!tg->enabled && + !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) + return -ENOLINK; + + dt = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SRC].code)-> + data_type; + + if (tg->enabled) { + /* Config Test Generator */ + struct v4l2_mbus_framefmt *f = + &csid->fmt[MSM_CSID_PAD_SRC]; + u8 bpp = csid_get_fmt_entry(f->code)->bpp; + u8 spp = csid_get_fmt_entry(f->code)->spp; + u32 num_bytes_per_line = f->width * bpp * spp / 8; + u32 num_lines = f->height; + + /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ + /* 1:0 VC */ + val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | + ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); + writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); + + /* 28:16 bytes per lines, 12:0 num of lines */ + val = ((num_bytes_per_line & 0x1fff) << 16) | + (num_lines & 0x1fff); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_DT_n_CGG_0(0)); + + /* 5:0 data type */ + val = dt; + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_DT_n_CGG_1(0)); + + /* 2:0 output test pattern */ + val = tg->payload_mode; + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_DT_n_CGG_2(0)); + } else { + struct csid_phy_config *phy = &csid->phy; + + val = phy->lane_cnt - 1; + val |= phy->lane_assign << 4; + + writel_relaxed(val, + csid->base + CAMSS_CSID_CORE_CTRL_0); + + val = phy->csiphy_id << 17; + val |= 0x9; + + writel_relaxed(val, + csid->base + CAMSS_CSID_CORE_CTRL_1); + } + + /* Config LUT */ + + dt_shift = (cid % 4) * 8; + df = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SINK].code)-> + decode_format; + + val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + val &= ~(0xff << dt_shift); + val |= dt << dt_shift; + writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + + val = (df << 4) | 0x3; + writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); + + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_ENABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } else { + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_DISABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } + + return 0; +} + +/* + * __csid_get_format - Get pointer to format structure + * @csid: CSID device + * @cfg: V4L2 subdev pad configuration + * @pad: pad from which format is requested + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE format structure + */ +static struct v4l2_mbus_framefmt * +__csid_get_format(struct csid_device *csid, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad); + + return &csid->fmt[pad]; +} + +/* + * csid_try_format - Handle try format by pad subdev method + * @csid: CSID device + * @cfg: V4L2 subdev pad configuration + * @pad: pad on which format is requested + * @fmt: pointer to v4l2 format structure + * @which: wanted subdev format + */ +static void csid_try_format(struct csid_device *csid, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + + switch (pad) { + case MSM_CSID_PAD_SINK: + /* Set format on sink pad */ + + for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) + if (fmt->code == csid_input_fmts[i].code) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(csid_input_fmts)) + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + break; + + case MSM_CSID_PAD_SRC: + if (csid->testgen_mode->cur.val == 0) { + /* Test generator is disabled, keep pad formats */ + /* in sync - set and return a format same as sink pad */ + struct v4l2_mbus_framefmt format; + + format = *__csid_get_format(csid, cfg, + MSM_CSID_PAD_SINK, which); + *fmt = format; + } else { + /* Test generator is enabled, set format on source*/ + /* pad to allow test generator usage */ + + for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) + if (csid_input_fmts[i].code == fmt->code) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(csid_input_fmts)) + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + + fmt->field = V4L2_FIELD_NONE; + } + break; + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* + * csid_enum_mbus_code - Handle pixel format enumeration + * @sd: CSID V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int csid_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == MSM_CSID_PAD_SINK) { + if (code->index >= ARRAY_SIZE(csid_input_fmts)) + return -EINVAL; + + code->code = csid_input_fmts[code->index].code; + } else { + if (csid->testgen_mode->cur.val == 0) { + if (code->index > 0) + return -EINVAL; + + format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, + code->which); + + code->code = format->code; + } else { + if (code->index >= ARRAY_SIZE(csid_input_fmts)) + return -EINVAL; + + code->code = csid_input_fmts[code->index].code; + } + } + + return 0; +} + +/* + * csid_enum_frame_size - Handle frame size enumeration + * @sd: CSID V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: pointer to v4l2_subdev_frame_size_enum structure + * return -EINVAL or zero on success + */ +static int csid_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + csid_try_format(csid, cfg, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + csid_try_format(csid, cfg, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * csid_get_format - Handle get format by pads subdev method + * @sd: CSID V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int csid_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * csid_set_format - Handle set format by pads subdev method + * @sd: CSID V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int csid_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == MSM_CSID_PAD_SINK) { + format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC, + fmt->which); + + *format = fmt->format; + csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format, + fmt->which); + } + + return 0; +} + +/* + * csid_init_formats - Initialize formats on all pads + * @sd: CSID V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_CSID_PAD_SINK, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 1920, + .height = 1080 + } + }; + + return csid_set_format(sd, fh ? fh->pad : NULL, &format); +} + +static const char * const csid_test_pattern_menu[] = { + "Disabled", + "Incrementing", + "Alternating 0x55/0xAA", + "All Zeros 0x00", + "All Ones 0xFF", + "Pseudo-random Data", +}; + +/* + * csid_set_test_pattern - Set test generator's pattern mode + * @csid: CSID device + * @value: desired test pattern mode + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_set_test_pattern(struct csid_device *csid, s32 value) +{ + struct csid_testgen_config *tg = &csid->testgen; + + /* If CSID is linked to CSIPHY, do not allow to enable test generator */ + if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) + return -EBUSY; + + tg->enabled = !!value; + + switch (value) { + case 1: + tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING; + break; + case 2: + tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA; + break; + case 3: + tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES; + break; + case 4: + tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES; + break; + case 5: + tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM; + break; + } + + return 0; +} + +/* + * csid_s_ctrl - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct csid_device *csid = container_of(ctrl->handler, + struct csid_device, ctrls); + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ret = csid_set_test_pattern(csid, ctrl->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops csid_ctrl_ops = { + .s_ctrl = csid_s_ctrl, +}; + +/* + * msm_csid_subdev_init - Initialize CSID device structure and resources + * @csid: CSID device + * @res: CSID module resources table + * @id: CSID module id + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csid_subdev_init(struct csid_device *csid, + const struct resources *res, u8 id) +{ + struct device *dev = to_device_index(csid, id); + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + int i, j; + int ret; + + csid->id = id; + + /* Memory */ + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); + csid->base = devm_ioremap_resource(dev, r); + if (IS_ERR(csid->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(csid->base); + } + + /* Interrupt */ + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + res->interrupt[0]); + if (!r) { + dev_err(dev, "missing IRQ\n"); + return -EINVAL; + } + + csid->irq = r->start; + snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", + dev_name(dev), MSM_CSID_NAME, csid->id); + ret = devm_request_irq(dev, csid->irq, csid_isr, + IRQF_TRIGGER_RISING, csid->irq_name, csid); + if (ret < 0) { + dev_err(dev, "request_irq failed: %d\n", ret); + return ret; + } + + disable_irq(csid->irq); + + /* Clocks */ + + csid->nclocks = 0; + while (res->clock[csid->nclocks]) + csid->nclocks++; + + csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), + GFP_KERNEL); + if (!csid->clock) + return -ENOMEM; + + for (i = 0; i < csid->nclocks; i++) { + struct camss_clock *clock = &csid->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + /* Regulator */ + + csid->vdda = devm_regulator_get(dev, res->regulator[0]); + if (IS_ERR(csid->vdda)) { + dev_err(dev, "could not get regulator\n"); + return PTR_ERR(csid->vdda); + } + + init_completion(&csid->reset_complete); + + return 0; +} + +/* + * msm_csid_get_csid_id - Get CSID HW module id + * @entity: Pointer to CSID media entity structure + * @id: Return CSID HW module id here + */ +void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct csid_device *csid = v4l2_get_subdevdata(sd); + + *id = csid->id; +} + +/* + * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter + * @lane_cfg - CSI2 lane configuration + * + * Return lane assign + */ +static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) +{ + u32 lane_assign = 0; + int i; + + for (i = 0; i < lane_cfg->num_data; i++) + lane_assign |= lane_cfg->data[i].pos << (i * 4); + + return lane_assign; +} + +/* + * csid_link_setup - Setup CSID connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad + * @remote: Pointer to remote pad + * @flags: Link flags + * + * Return 0 on success + */ +static int csid_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_entity_remote_pad(local)) + return -EBUSY; + + if ((local->flags & MEDIA_PAD_FL_SINK) && + (flags & MEDIA_LNK_FL_ENABLED)) { + struct v4l2_subdev *sd; + struct csid_device *csid; + struct csiphy_device *csiphy; + struct csiphy_lanes_cfg *lane_cfg; + struct v4l2_subdev_format format = { 0 }; + + sd = media_entity_to_v4l2_subdev(entity); + csid = v4l2_get_subdevdata(sd); + + /* If test generator is enabled */ + /* do not allow a link from CSIPHY to CSID */ + if (csid->testgen_mode->cur.val != 0) + return -EBUSY; + + sd = media_entity_to_v4l2_subdev(remote->entity); + csiphy = v4l2_get_subdevdata(sd); + + /* If a sensor is not linked to CSIPHY */ + /* do no allow a link from CSIPHY to CSID */ + if (!csiphy->cfg.csi2) + return -EPERM; + + csid->phy.csiphy_id = csiphy->id; + + lane_cfg = &csiphy->cfg.csi2->lane_cfg; + csid->phy.lane_cnt = lane_cfg->num_data; + csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); + + /* Reset format on source pad to sink pad format */ + format.pad = MSM_CSID_PAD_SRC; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + csid_set_format(&csid->subdev, NULL, &format); + } + + return 0; +} + +static const struct v4l2_subdev_core_ops csid_core_ops = { + .s_power = csid_set_power, +}; + +static const struct v4l2_subdev_video_ops csid_video_ops = { + .s_stream = csid_set_stream, +}; + +static const struct v4l2_subdev_pad_ops csid_pad_ops = { + .enum_mbus_code = csid_enum_mbus_code, + .enum_frame_size = csid_enum_frame_size, + .get_fmt = csid_get_format, + .set_fmt = csid_set_format, +}; + +static const struct v4l2_subdev_ops csid_v4l2_ops = { + .core = &csid_core_ops, + .video = &csid_video_ops, + .pad = &csid_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = { + .open = csid_init_formats, +}; + +static const struct media_entity_operations csid_media_ops = { + .link_setup = csid_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * msm_csid_register_entity - Register subdev node for CSID module + * @csid: CSID device + * @v4l2_dev: V4L2 device + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csid_register_entity(struct csid_device *csid, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &csid->subdev; + struct media_pad *pads = csid->pads; + struct device *dev = to_device_index(csid, csid->id); + int ret; + + v4l2_subdev_init(sd, &csid_v4l2_ops); + sd->internal_ops = &csid_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + MSM_CSID_NAME, csid->id); + v4l2_set_subdevdata(sd, csid); + + ret = v4l2_ctrl_handler_init(&csid->ctrls, 1); + if (ret < 0) { + dev_err(dev, "Failed to init ctrl handler: %d\n", ret); + return ret; + } + + csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, + &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0, + csid_test_pattern_menu); + + if (csid->ctrls.error) { + dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); + ret = csid->ctrls.error; + goto free_ctrl; + } + + csid->subdev.ctrl_handler = &csid->ctrls; + + ret = csid_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto free_ctrl; + } + + pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.ops = &csid_media_ops; + ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto free_ctrl; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + goto media_cleanup; + } + + return 0; + +media_cleanup: + media_entity_cleanup(&sd->entity); +free_ctrl: + v4l2_ctrl_handler_free(&csid->ctrls); + + return ret; +} + +/* + * msm_csid_unregister_entity - Unregister CSID module subdev node + * @csid: CSID device + */ +void msm_csid_unregister_entity(struct csid_device *csid) +{ + v4l2_device_unregister_subdev(&csid->subdev); + media_entity_cleanup(&csid->subdev.entity); + v4l2_ctrl_handler_free(&csid->ctrls); +} diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h new file mode 100644 index 000000000000..8012222b81ad --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -0,0 +1,82 @@ +/* + * camss-csid.h + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_CSID_H +#define QC_MSM_CAMSS_CSID_H + +#include +#include +#include +#include +#include +#include + +#define MSM_CSID_PAD_SINK 0 +#define MSM_CSID_PAD_SRC 1 +#define MSM_CSID_PADS_NUM 2 + +enum csid_payload_mode { + CSID_PAYLOAD_MODE_INCREMENTING = 0, + CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1, + CSID_PAYLOAD_MODE_ALL_ZEROES = 2, + CSID_PAYLOAD_MODE_ALL_ONES = 3, + CSID_PAYLOAD_MODE_RANDOM = 4, + CSID_PAYLOAD_MODE_USER_SPECIFIED = 5, +}; + +struct csid_testgen_config { + u8 enabled; + enum csid_payload_mode payload_mode; +}; + +struct csid_phy_config { + u8 csiphy_id; + u8 lane_cnt; + u32 lane_assign; +}; + +struct csid_device { + u8 id; + struct v4l2_subdev subdev; + struct media_pad pads[MSM_CSID_PADS_NUM]; + void __iomem *base; + u32 irq; + char irq_name[30]; + struct camss_clock *clock; + int nclocks; + struct regulator *vdda; + struct completion reset_complete; + struct csid_testgen_config testgen; + struct csid_phy_config phy; + struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *testgen_mode; +}; + +struct resources; + +int msm_csid_subdev_init(struct csid_device *csid, + const struct resources *res, u8 id); + +int msm_csid_register_entity(struct csid_device *csid, + struct v4l2_device *v4l2_dev); + +void msm_csid_unregister_entity(struct csid_device *csid); + +void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); + +#endif /* QC_MSM_CAMSS_CSID_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c new file mode 100644 index 000000000000..642de259dc6b --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -0,0 +1,893 @@ +/* + * camss-csiphy.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-csiphy.h" +#include "camss.h" + +#define MSM_CSIPHY_NAME "msm_csiphy" + +#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) +#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) +#define CAMSS_CSI_PHY_GLBL_RESET 0x140 +#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 +#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 +#define CAMSS_CSI_PHY_HW_VERSION 0x188 +#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) +#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec +#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 + +static const struct { + u32 code; + u8 bpp; +} csiphy_formats[] = { + { + MEDIA_BUS_FMT_UYVY8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_VYUY8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_YUYV8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_YVYU8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + 12, + } +}; + +/* + * csiphy_get_bpp - map media bus format to bits per pixel + * @code: media bus format code + * + * Return number of bits per pixel + */ +static u8 csiphy_get_bpp(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) + if (code == csiphy_formats[i].code) + return csiphy_formats[i].bpp; + + WARN(1, "Unknown format\n"); + + return csiphy_formats[0].bpp; +} + +/* + * csiphy_isr - CSIPHY module interrupt handler + * @irq: Interrupt line + * @dev: CSIPHY device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csiphy_isr(int irq, void *dev) +{ + struct csiphy_device *csiphy = dev; + u8 i; + + for (i = 0; i < 8; i++) { + u8 val = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); + writel_relaxed(val, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + } + + return IRQ_HANDLED; +} + +/* + * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module + * @csiphy: CSIPHY device + */ +static int csiphy_set_clock_rates(struct csiphy_device *csiphy) +{ + struct device *dev = to_device_index(csiphy, csiphy->id); + u32 pixel_clock; + int i, j; + int ret; + + ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); + if (ret) + pixel_clock = 0; + + for (i = 0; i < csiphy->nclocks; i++) { + struct camss_clock *clock = &csiphy->clock[i]; + + if (!strcmp(clock->name, "csiphy0_timer") || + !strcmp(clock->name, "csiphy1_timer")) { + u8 bpp = csiphy_get_bpp( + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; + u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); + long round_rate; + + camss_add_clock_margin(&min_rate); + + for (j = 0; j < clock->nfreqs; j++) + if (min_rate < clock->freq[j]) + break; + + if (j == clock->nfreqs) { + dev_err(dev, + "Pixel clock is too high for CSIPHY\n"); + return -EINVAL; + } + + /* if sensor pixel clock is not available */ + /* set highest possible CSIPHY clock rate */ + if (min_rate == 0) + j = clock->nfreqs - 1; + + round_rate = clk_round_rate(clock->clk, clock->freq[j]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + csiphy->timer_clk_rate = round_rate; + + ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +/* + * csiphy_reset - Perform software reset on CSIPHY module + * @csiphy: CSIPHY device + */ +static void csiphy_reset(struct csiphy_device *csiphy) +{ + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + usleep_range(5000, 8000); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); +} + +/* + * csiphy_set_power - Power on/off CSIPHY module + * @sd: CSIPHY V4L2 subdevice + * @on: Requested power state + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_set_power(struct v4l2_subdev *sd, int on) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct device *dev = to_device_index(csiphy, csiphy->id); + + if (on) { + u8 hw_version; + int ret; + + ret = csiphy_set_clock_rates(csiphy); + if (ret < 0) + return ret; + + ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); + if (ret < 0) + return ret; + + enable_irq(csiphy->irq); + + csiphy_reset(csiphy); + + hw_version = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_HW_VERSION); + dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); + } else { + disable_irq(csiphy->irq); + + camss_disable_clocks(csiphy->nclocks, csiphy->clock); + } + + return 0; +} + +/* + * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter + * @lane_cfg - CSI2 lane configuration + * + * Return lane mask + */ +static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) +{ + u8 lane_mask; + int i; + + lane_mask = 1 << lane_cfg->clk.pos; + + for (i = 0; i < lane_cfg->num_data; i++) + lane_mask |= 1 << lane_cfg->data[i].pos; + + return lane_mask; +} + +/* + * csiphy_settle_cnt_calc - Calculate settle count value + * @csiphy: CSIPHY device + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter pixel clock + * frequency. + * + * Return settle count value or 0 if the CSI2 pixel clock + * frequency is not available + */ +static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) +{ + u8 bpp = csiphy_get_bpp( + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; + u32 pixel_clock; /* Hz */ + u32 mipi_clock; /* Hz */ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_prepare_zero_min; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + int ret; + + ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); + if (ret) { + dev_err(to_device_index(csiphy, csiphy->id), + "Cannot get CSI2 transmitter's pixel clock\n"); + return 0; + } + if (!pixel_clock) { + dev_err(to_device_index(csiphy, csiphy->id), + "Got pixel clock == 0, cannot continue\n"); + return 0; + } + + mipi_clock = pixel_clock * bpp / (2 * num_lanes); + ui = div_u64(1000000000000LL, mipi_clock); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_prepare_zero_min = 145000 + 10 * ui; + t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; + + timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate); + settle_cnt = t_hs_settle / timer_period; + + return settle_cnt; +} + +/* + * csiphy_stream_on - Enable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to enable streaming on CSIPHY module. + * Main configuration of CSIPHY module is also done here. + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_stream_on(struct csiphy_device *csiphy) +{ + struct csiphy_config *cfg = &csiphy->cfg; + u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); + u8 settle_cnt; + u8 val; + int i = 0; + + settle_cnt = csiphy_settle_cnt_calc(csiphy); + if (!settle_cnt) + return -EINVAL; + + val = readl_relaxed(csiphy->base_clk_mux); + if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { + val &= ~0xf0; + val |= cfg->csid_id << 4; + } else { + val &= ~0xf; + val |= cfg->csid_id; + } + writel_relaxed(val, csiphy->base_clk_mux); + + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_T_WAKEUP_CFG0); + + val = 0x1; + val |= lane_mask << 1; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); + + val = cfg->combo_mode << 4; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + + while (lane_mask) { + if (lane_mask & 0x1) { + writel_relaxed(0x10, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(i)); + writel_relaxed(settle_cnt, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG3(i)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + } + + lane_mask >>= 1; + i++; + } + + return 0; +} + +/* + * csiphy_stream_off - Disable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to disable streaming on CSIPHY module + */ +static void csiphy_stream_off(struct csiphy_device *csiphy) +{ + u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg); + int i = 0; + + while (lane_mask) { + if (lane_mask & 0x1) + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(i)); + + lane_mask >>= 1; + i++; + } + + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); +} + + +/* + * csiphy_set_stream - Enable/disable streaming on CSIPHY module + * @sd: CSIPHY V4L2 subdevice + * @enable: Requested streaming state + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + int ret = 0; + + if (enable) + ret = csiphy_stream_on(csiphy); + else + csiphy_stream_off(csiphy); + + return ret; +} + +/* + * __csiphy_get_format - Get pointer to format structure + * @csiphy: CSIPHY device + * @cfg: V4L2 subdev pad configuration + * @pad: pad from which format is requested + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE format structure + */ +static struct v4l2_mbus_framefmt * +__csiphy_get_format(struct csiphy_device *csiphy, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad); + + return &csiphy->fmt[pad]; +} + +/* + * csiphy_try_format - Handle try format by pad subdev method + * @csiphy: CSIPHY device + * @cfg: V4L2 subdev pad configuration + * @pad: pad on which format is requested + * @fmt: pointer to v4l2 format structure + * @which: wanted subdev format + */ +static void csiphy_try_format(struct csiphy_device *csiphy, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + + switch (pad) { + case MSM_CSIPHY_PAD_SINK: + /* Set format on sink pad */ + + for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) + if (fmt->code == csiphy_formats[i].code) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(csiphy_formats)) + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + break; + + case MSM_CSIPHY_PAD_SRC: + /* Set and return a format same as sink pad */ + + *fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK, + which); + + break; + } +} + +/* + * csiphy_enum_mbus_code - Handle pixel format enumeration + * @sd: CSIPHY V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == MSM_CSIPHY_PAD_SINK) { + if (code->index >= ARRAY_SIZE(csiphy_formats)) + return -EINVAL; + + code->code = csiphy_formats[code->index].code; + } else { + if (code->index > 0) + return -EINVAL; + + format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK, + code->which); + + code->code = format->code; + } + + return 0; +} + +/* + * csiphy_enum_frame_size - Handle frame size enumeration + * @sd: CSIPHY V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: pointer to v4l2_subdev_frame_size_enum structure + * return -EINVAL or zero on success + */ +static int csiphy_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * csiphy_get_format - Handle get format by pads subdev method + * @sd: CSIPHY V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int csiphy_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * csiphy_set_format - Handle set format by pads subdev method + * @sd: CSIPHY V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int csiphy_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == MSM_CSIPHY_PAD_SINK) { + format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, + fmt->which); + + *format = fmt->format; + csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format, + fmt->which); + } + + return 0; +} + +/* + * csiphy_init_formats - Initialize formats on all pads + * @sd: CSIPHY V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_CSIPHY_PAD_SINK, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 1920, + .height = 1080 + } + }; + + return csiphy_set_format(sd, fh ? fh->pad : NULL, &format); +} + +/* + * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * @csiphy: CSIPHY device + * @res: CSIPHY module resources table + * @id: CSIPHY module id + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csiphy_subdev_init(struct csiphy_device *csiphy, + const struct resources *res, u8 id) +{ + struct device *dev = to_device_index(csiphy, id); + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + int i, j; + int ret; + + csiphy->id = id; + csiphy->cfg.combo_mode = 0; + + /* Memory */ + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); + csiphy->base = devm_ioremap_resource(dev, r); + if (IS_ERR(csiphy->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(csiphy->base); + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); + csiphy->base_clk_mux = devm_ioremap_resource(dev, r); + if (IS_ERR(csiphy->base_clk_mux)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(csiphy->base_clk_mux); + } + + /* Interrupt */ + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + res->interrupt[0]); + if (!r) { + dev_err(dev, "missing IRQ\n"); + return -EINVAL; + } + + csiphy->irq = r->start; + snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", + dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); + ret = devm_request_irq(dev, csiphy->irq, csiphy_isr, + IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy); + if (ret < 0) { + dev_err(dev, "request_irq failed: %d\n", ret); + return ret; + } + + disable_irq(csiphy->irq); + + /* Clocks */ + + csiphy->nclocks = 0; + while (res->clock[csiphy->nclocks]) + csiphy->nclocks++; + + csiphy->clock = devm_kcalloc(dev, + csiphy->nclocks, sizeof(*csiphy->clock), + GFP_KERNEL); + if (!csiphy->clock) + return -ENOMEM; + + for (i = 0; i < csiphy->nclocks; i++) { + struct camss_clock *clock = &csiphy->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + return 0; +} + +/* + * csiphy_link_setup - Setup CSIPHY connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad + * @remote: Pointer to remote pad + * @flags: Link flags + * + * Rreturn 0 on success + */ +static int csiphy_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if ((local->flags & MEDIA_PAD_FL_SOURCE) && + (flags & MEDIA_LNK_FL_ENABLED)) { + struct v4l2_subdev *sd; + struct csiphy_device *csiphy; + struct csid_device *csid; + + if (media_entity_remote_pad(local)) + return -EBUSY; + + sd = media_entity_to_v4l2_subdev(entity); + csiphy = v4l2_get_subdevdata(sd); + + sd = media_entity_to_v4l2_subdev(remote->entity); + csid = v4l2_get_subdevdata(sd); + + csiphy->cfg.csid_id = csid->id; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops csiphy_core_ops = { + .s_power = csiphy_set_power, +}; + +static const struct v4l2_subdev_video_ops csiphy_video_ops = { + .s_stream = csiphy_set_stream, +}; + +static const struct v4l2_subdev_pad_ops csiphy_pad_ops = { + .enum_mbus_code = csiphy_enum_mbus_code, + .enum_frame_size = csiphy_enum_frame_size, + .get_fmt = csiphy_get_format, + .set_fmt = csiphy_set_format, +}; + +static const struct v4l2_subdev_ops csiphy_v4l2_ops = { + .core = &csiphy_core_ops, + .video = &csiphy_video_ops, + .pad = &csiphy_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = { + .open = csiphy_init_formats, +}; + +static const struct media_entity_operations csiphy_media_ops = { + .link_setup = csiphy_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * msm_csiphy_register_entity - Register subdev node for CSIPHY module + * @csiphy: CSIPHY device + * @v4l2_dev: V4L2 device + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csiphy_register_entity(struct csiphy_device *csiphy, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &csiphy->subdev; + struct media_pad *pads = csiphy->pads; + struct device *dev = to_device_index(csiphy, csiphy->id); + int ret; + + v4l2_subdev_init(sd, &csiphy_v4l2_ops); + sd->internal_ops = &csiphy_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + MSM_CSIPHY_NAME, csiphy->id); + v4l2_set_subdevdata(sd, csiphy); + + ret = csiphy_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + return ret; + } + + pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.ops = &csiphy_media_ops; + ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + media_entity_cleanup(&sd->entity); + } + + return ret; +} + +/* + * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node + * @csiphy: CSIPHY device + */ +void msm_csiphy_unregister_entity(struct csiphy_device *csiphy) +{ + v4l2_device_unregister_subdev(&csiphy->subdev); + media_entity_cleanup(&csiphy->subdev.entity); +} diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h new file mode 100644 index 000000000000..9a422095b773 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -0,0 +1,77 @@ +/* + * camss-csiphy.h + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_CSIPHY_H +#define QC_MSM_CAMSS_CSIPHY_H + +#include +#include +#include +#include +#include + +#define MSM_CSIPHY_PAD_SINK 0 +#define MSM_CSIPHY_PAD_SRC 1 +#define MSM_CSIPHY_PADS_NUM 2 + +struct csiphy_lane { + u8 pos; + u8 pol; +}; + +struct csiphy_lanes_cfg { + int num_data; + struct csiphy_lane *data; + struct csiphy_lane clk; +}; + +struct csiphy_csi2_cfg { + struct csiphy_lanes_cfg lane_cfg; +}; + +struct csiphy_config { + u8 combo_mode; + u8 csid_id; + struct csiphy_csi2_cfg *csi2; +}; + +struct csiphy_device { + u8 id; + struct v4l2_subdev subdev; + struct media_pad pads[MSM_CSIPHY_PADS_NUM]; + void __iomem *base; + void __iomem *base_clk_mux; + u32 irq; + char irq_name[30]; + struct camss_clock *clock; + int nclocks; + u32 timer_clk_rate; + struct csiphy_config cfg; + struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; +}; + +struct resources; + +int msm_csiphy_subdev_init(struct csiphy_device *csiphy, + const struct resources *res, u8 id); + +int msm_csiphy_register_entity(struct csiphy_device *csiphy, + struct v4l2_device *v4l2_dev); + +void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); + +#endif /* QC_MSM_CAMSS_CSIPHY_H */ diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c new file mode 100644 index 000000000000..636d5e712037 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -0,0 +1,1178 @@ +/* + * camss-ispif.c + * + * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-ispif.h" +#include "camss.h" + +#define MSM_ISPIF_NAME "msm_ispif" + +#define ispif_line_array(ptr_line) \ + ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)])) + +#define to_ispif(ptr_line) \ + container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line) + +#define ISPIF_RST_CMD_0 0x008 +#define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) +#define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) +#define ISPIF_RST_CMD_0_SW_REG_RST (1 << 2) +#define ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST (1 << 3) +#define ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST (1 << 4) +#define ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST (1 << 5) +#define ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST (1 << 6) +#define ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST (1 << 7) +#define ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST (1 << 8) +#define ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST (1 << 9) +#define ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST (1 << 10) +#define ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST (1 << 11) +#define ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST (1 << 12) +#define ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST (1 << 16) +#define ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST (1 << 17) +#define ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST (1 << 18) +#define ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST (1 << 19) +#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x01c +#define ISPIF_VFE_m_CTRL_0(m) (0x200 + 0x200 * (m)) +#define ISPIF_VFE_m_CTRL_0_PIX0_LINE_BUF_EN (1 << 6) +#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE 0x00001249 +#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK 0x00001fff +#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE 0x02492000 +#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK 0x03ffe000 +#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20c + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE 0x00001249 +#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK 0x00001fff +#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE 0x02492000 +#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK 0x03ffe000 +#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE 0x00001249 +#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK 0x00001fff +#define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x21c + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW (1 << 12) +#define ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW (1 << 25) +#define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x220 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW (1 << 12) +#define ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW (1 << 25) +#define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x224 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW (1 << 12) +#define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x230 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x234 + 0x200 * (m)) +#define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x238 + 0x200 * (m)) +#define ISPIF_VFE_m_INTF_INPUT_SEL(m) (0x244 + 0x200 * (m)) +#define ISPIF_VFE_m_INTF_CMD_0(m) (0x248 + 0x200 * (m)) +#define ISPIF_VFE_m_INTF_CMD_1(m) (0x24c + 0x200 * (m)) +#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) \ + (0x254 + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \ + (0x264 + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ + (0x2c0 + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ + (0x2d0 + 0x200 * (m) + 0x4 * (n)) + +#define CSI_PIX_CLK_MUX_SEL 0x000 +#define CSI_RDI_CLK_MUX_SEL 0x008 + +#define ISPIF_TIMEOUT_SLEEP_US 1000 +#define ISPIF_TIMEOUT_ALL_US 1000000 +#define ISPIF_RESET_TIMEOUT_MS 500 + +enum ispif_intf_cmd { + CMD_DISABLE_FRAME_BOUNDARY = 0x0, + CMD_ENABLE_FRAME_BOUNDARY = 0x1, + CMD_DISABLE_IMMEDIATELY = 0x2, + CMD_ALL_DISABLE_IMMEDIATELY = 0xaaaaaaaa, + CMD_ALL_NO_CHANGE = 0xffffffff, +}; + +static const u32 ispif_formats[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +/* + * ispif_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: ISPIF device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t ispif_isr(int irq, void *dev) +{ + struct ispif_device *ispif = dev; + u32 value0, value1, value2; + + value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); + value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0)); + value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0)); + + writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0)); + writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0)); + writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0)); + + writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); + + if ((value0 >> 27) & 0x1) + complete(&ispif->reset_complete); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n"); + + if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n"); + + return IRQ_HANDLED; +} + +/* + * ispif_reset - Trigger reset on ISPIF module and wait to complete + * @ispif: ISPIF device + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_reset(struct ispif_device *ispif) +{ + unsigned long time; + u32 val; + int ret; + + ret = camss_enable_clocks(ispif->nclocks_for_reset, + ispif->clock_for_reset, + to_device(ispif)); + if (ret < 0) + return ret; + + reinit_completion(&ispif->reset_complete); + + val = ISPIF_RST_CMD_0_STROBED_RST_EN | + ISPIF_RST_CMD_0_MISC_LOGIC_RST | + ISPIF_RST_CMD_0_SW_REG_RST | + ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST | + ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST | + ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST | + ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST | + ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST | + ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST | + ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST | + ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST | + ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST | + ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST | + ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST | + ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST | + ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST | + ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST; + + writel_relaxed(val, ispif->base + ISPIF_RST_CMD_0); + + time = wait_for_completion_timeout(&ispif->reset_complete, + msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(to_device(ispif), "ISPIF reset timeout\n"); + return -EIO; + } + + camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); + + return 0; +} + +/* + * ispif_set_power - Power on/off ISPIF module + * @sd: ISPIF V4L2 subdevice + * @on: Requested power state + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_set_power(struct v4l2_subdev *sd, int on) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct ispif_device *ispif = to_ispif(line); + struct device *dev = to_device(ispif); + int ret = 0; + + mutex_lock(&ispif->power_lock); + + if (on) { + if (ispif->power_count) { + /* Power is already on */ + ispif->power_count++; + goto exit; + } + + ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); + if (ret < 0) + goto exit; + + ret = ispif_reset(ispif); + if (ret < 0) { + camss_disable_clocks(ispif->nclocks, ispif->clock); + goto exit; + } + + ispif->intf_cmd[line->vfe_id].cmd_0 = CMD_ALL_NO_CHANGE; + ispif->intf_cmd[line->vfe_id].cmd_1 = CMD_ALL_NO_CHANGE; + + ispif->power_count++; + } else { + if (ispif->power_count == 0) { + dev_err(dev, "ispif power off on power_count == 0\n"); + goto exit; + } else if (ispif->power_count == 1) { + camss_disable_clocks(ispif->nclocks, ispif->clock); + } + + ispif->power_count--; + } + +exit: + mutex_unlock(&ispif->power_lock); + + return ret; +} + +/* + * ispif_select_clk_mux - Select clock for PIX/RDI interface + * @ispif: ISPIF device + * @intf: VFE interface + * @csid: CSID HW module id + * @vfe: VFE HW module id + * @enable: enable or disable the selected clock + */ +static void ispif_select_clk_mux(struct ispif_device *ispif, + enum ispif_intf intf, u8 csid, + u8 vfe, u8 enable) +{ + u32 val; + + switch (intf) { + case PIX0: + val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); + val &= ~(0xf << (vfe * 8)); + if (enable) + val |= (csid << (vfe * 8)); + writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); + break; + + case RDI0: + val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + val &= ~(0xf << (vfe * 12)); + if (enable) + val |= (csid << (vfe * 12)); + writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + break; + + case PIX1: + val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); + val &= ~(0xf << (4 + (vfe * 8))); + if (enable) + val |= (csid << (4 + (vfe * 8))); + writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); + break; + + case RDI1: + val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + val &= ~(0xf << (4 + (vfe * 12))); + if (enable) + val |= (csid << (4 + (vfe * 12))); + writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + break; + + case RDI2: + val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + val &= ~(0xf << (8 + (vfe * 12))); + if (enable) + val |= (csid << (8 + (vfe * 12))); + writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); + break; + } + + mb(); +} + +/* + * ispif_validate_intf_status - Validate current status of PIX/RDI interface + * @ispif: ISPIF device + * @intf: VFE interface + * @vfe: VFE HW module id + * + * Return 0 when interface is idle or -EBUSY otherwise + */ +static int ispif_validate_intf_status(struct ispif_device *ispif, + enum ispif_intf intf, u8 vfe) +{ + int ret = 0; + u32 val = 0; + + switch (intf) { + case PIX0: + val = readl_relaxed(ispif->base + + ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0)); + break; + case RDI0: + val = readl_relaxed(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0)); + break; + case PIX1: + val = readl_relaxed(ispif->base + + ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1)); + break; + case RDI1: + val = readl_relaxed(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1)); + break; + case RDI2: + val = readl_relaxed(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2)); + break; + } + + if ((val & 0xf) != 0xf) { + dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n", + __func__, val); + ret = -EBUSY; + } + + return ret; +} + +/* + * ispif_wait_for_stop - Wait for PIX/RDI interface to stop + * @ispif: ISPIF device + * @intf: VFE interface + * @vfe: VFE HW module id + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_wait_for_stop(struct ispif_device *ispif, + enum ispif_intf intf, u8 vfe) +{ + u32 addr = 0; + u32 stop_flag = 0; + int ret; + + switch (intf) { + case PIX0: + addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0); + break; + case RDI0: + addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0); + break; + case PIX1: + addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1); + break; + case RDI1: + addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1); + break; + case RDI2: + addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2); + break; + } + + ret = readl_poll_timeout(ispif->base + addr, + stop_flag, + (stop_flag & 0xf) == 0xf, + ISPIF_TIMEOUT_SLEEP_US, + ISPIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(to_device(ispif), "%s: ispif stop timeout\n", + __func__); + + return ret; +} + +/* + * ispif_select_csid - Select CSID HW module for input from + * @ispif: ISPIF device + * @intf: VFE interface + * @csid: CSID HW module id + * @vfe: VFE HW module id + * @enable: enable or disable the selected input + */ +static void ispif_select_csid(struct ispif_device *ispif, enum ispif_intf intf, + u8 csid, u8 vfe, u8 enable) +{ + u32 val; + + val = readl_relaxed(ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); + switch (intf) { + case PIX0: + val &= ~(BIT(1) | BIT(0)); + if (enable) + val |= csid; + break; + case RDI0: + val &= ~(BIT(5) | BIT(4)); + if (enable) + val |= (csid << 4); + break; + case PIX1: + val &= ~(BIT(9) | BIT(8)); + if (enable) + val |= (csid << 8); + break; + case RDI1: + val &= ~(BIT(13) | BIT(12)); + if (enable) + val |= (csid << 12); + break; + case RDI2: + val &= ~(BIT(21) | BIT(20)); + if (enable) + val |= (csid << 20); + break; + } + + writel(val, ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); +} + +/* + * ispif_select_cid - Enable/disable desired CID + * @ispif: ISPIF device + * @intf: VFE interface + * @cid: desired CID to enable/disable + * @vfe: VFE HW module id + * @enable: enable or disable the desired CID + */ +static void ispif_select_cid(struct ispif_device *ispif, enum ispif_intf intf, + u8 cid, u8 vfe, u8 enable) +{ + u32 cid_mask = 1 << cid; + u32 addr = 0; + u32 val; + + switch (intf) { + case PIX0: + addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 0); + break; + case RDI0: + addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 0); + break; + case PIX1: + addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 1); + break; + case RDI1: + addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 1); + break; + case RDI2: + addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 2); + break; + } + + val = readl_relaxed(ispif->base + addr); + if (enable) + val |= cid_mask; + else + val &= ~cid_mask; + + writel(val, ispif->base + addr); +} + +/* + * ispif_config_irq - Enable/disable interrupts for PIX/RDI interface + * @ispif: ISPIF device + * @intf: VFE interface + * @vfe: VFE HW module id + * @enable: enable or disable + */ +static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf, + u8 vfe, u8 enable) +{ + u32 val; + + switch (intf) { + case PIX0: + val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); + val &= ~ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK; + if (enable) + val |= ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE; + writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); + writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); + break; + case RDI0: + val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); + val &= ~ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK; + if (enable) + val |= ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE; + writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); + writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); + break; + case PIX1: + val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); + val &= ~ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK; + if (enable) + val |= ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE; + writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); + writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); + break; + case RDI1: + val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); + val &= ~ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK; + if (enable) + val |= ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE; + writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); + writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); + break; + case RDI2: + val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); + val &= ~ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK; + if (enable) + val |= ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE; + writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); + writel_relaxed(ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(vfe)); + break; + } + + writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); +} + +/* + * ispif_set_intf_cmd - Set command to enable/disable interface + * @ispif: ISPIF device + * @cmd: interface command + * @intf: VFE interface + * @vfe: VFE HW module id + * @vc: virtual channel + */ +static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd, + enum ispif_intf intf, u8 vfe, u8 vc) +{ + u32 *val; + + if (intf == RDI2) { + val = &ispif->intf_cmd[vfe].cmd_1; + *val &= ~(0x3 << (vc * 2 + 8)); + *val |= (cmd << (vc * 2 + 8)); + wmb(); + writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe)); + wmb(); + } else { + val = &ispif->intf_cmd[vfe].cmd_0; + *val &= ~(0x3 << (vc * 2 + intf * 8)); + *val |= (cmd << (vc * 2 + intf * 8)); + wmb(); + writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe)); + wmb(); + } +} + +/* + * ispif_set_stream - Enable/disable streaming on ISPIF module + * @sd: ISPIF V4L2 subdevice + * @enable: Requested streaming state + * + * Main configuration of ISPIF module is also done here. + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct ispif_device *ispif = to_ispif(line); + enum ispif_intf intf = line->interface; + u8 csid = line->csid_id; + u8 vfe = line->vfe_id; + u8 vc = 0; /* Virtual Channel 0 */ + u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ + int ret; + + if (enable) { + if (!media_entity_remote_pad(&line->pads[MSM_ISPIF_PAD_SINK])) + return -ENOLINK; + + /* Config */ + + mutex_lock(&ispif->config_lock); + ispif_select_clk_mux(ispif, intf, csid, vfe, 1); + + ret = ispif_validate_intf_status(ispif, intf, vfe); + if (ret < 0) { + mutex_unlock(&ispif->config_lock); + return ret; + } + + ispif_select_csid(ispif, intf, csid, vfe, 1); + ispif_select_cid(ispif, intf, cid, vfe, 1); + ispif_config_irq(ispif, intf, vfe, 1); + ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY, + intf, vfe, vc); + } else { + mutex_lock(&ispif->config_lock); + ispif_set_intf_cmd(ispif, CMD_DISABLE_FRAME_BOUNDARY, + intf, vfe, vc); + mutex_unlock(&ispif->config_lock); + + ret = ispif_wait_for_stop(ispif, intf, vfe); + if (ret < 0) + return ret; + + mutex_lock(&ispif->config_lock); + ispif_config_irq(ispif, intf, vfe, 0); + ispif_select_cid(ispif, intf, cid, vfe, 0); + ispif_select_csid(ispif, intf, csid, vfe, 0); + ispif_select_clk_mux(ispif, intf, csid, vfe, 0); + } + + mutex_unlock(&ispif->config_lock); + + return 0; +} + +/* + * __ispif_get_format - Get pointer to format structure + * @ispif: ISPIF line + * @cfg: V4L2 subdev pad configuration + * @pad: pad from which format is requested + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE format structure + */ +static struct v4l2_mbus_framefmt * +__ispif_get_format(struct ispif_line *line, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); + + return &line->fmt[pad]; +} + +/* + * ispif_try_format - Handle try format by pad subdev method + * @ispif: ISPIF line + * @cfg: V4L2 subdev pad configuration + * @pad: pad on which format is requested + * @fmt: pointer to v4l2 format structure + * @which: wanted subdev format + */ +static void ispif_try_format(struct ispif_line *line, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + + switch (pad) { + case MSM_ISPIF_PAD_SINK: + /* Set format on sink pad */ + + for (i = 0; i < ARRAY_SIZE(ispif_formats); i++) + if (fmt->code == ispif_formats[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ispif_formats)) + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + break; + + case MSM_ISPIF_PAD_SRC: + /* Set and return a format same as sink pad */ + + *fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, + which); + + break; + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* + * ispif_enum_mbus_code - Handle pixel format enumeration + * @sd: ISPIF V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ispif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == MSM_ISPIF_PAD_SINK) { + if (code->index >= ARRAY_SIZE(ispif_formats)) + return -EINVAL; + + code->code = ispif_formats[code->index]; + } else { + if (code->index > 0) + return -EINVAL; + + format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, + code->which); + + code->code = format->code; + } + + return 0; +} + +/* + * ispif_enum_frame_size - Handle frame size enumeration + * @sd: ISPIF V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: pointer to v4l2_subdev_frame_size_enum structure + * return -EINVAL or zero on success + */ +static int ispif_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ispif_try_format(line, cfg, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ispif_try_format(line, cfg, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ispif_get_format - Handle get format by pads subdev method + * @sd: ISPIF V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int ispif_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * ispif_set_format - Handle set format by pads subdev method + * @sd: ISPIF V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int ispif_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ispif_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == MSM_ISPIF_PAD_SINK) { + format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC, + fmt->which); + + *format = fmt->format; + ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format, + fmt->which); + } + + return 0; +} + +/* + * ispif_init_formats - Initialize formats on all pads + * @sd: ISPIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_ISPIF_PAD_SINK, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 1920, + .height = 1080 + } + }; + + return ispif_set_format(sd, fh ? fh->pad : NULL, &format); +} + +/* + * msm_ispif_subdev_init - Initialize ISPIF device structure and resources + * @ispif: ISPIF device + * @res: ISPIF module resources table + * + * Return 0 on success or a negative error code otherwise + */ +int msm_ispif_subdev_init(struct ispif_device *ispif, + const struct resources_ispif *res) +{ + struct device *dev = to_device(ispif); + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + int i; + int ret; + + /* Memory */ + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); + ispif->base = devm_ioremap_resource(dev, r); + if (IS_ERR(ispif->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(ispif->base); + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); + ispif->base_clk_mux = devm_ioremap_resource(dev, r); + if (IS_ERR(ispif->base_clk_mux)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(ispif->base_clk_mux); + } + + /* Interrupt */ + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt); + + if (!r) { + dev_err(dev, "missing IRQ\n"); + return -EINVAL; + } + + ispif->irq = r->start; + snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", + dev_name(dev), MSM_ISPIF_NAME); + ret = devm_request_irq(dev, ispif->irq, ispif_isr, + IRQF_TRIGGER_RISING, ispif->irq_name, ispif); + if (ret < 0) { + dev_err(dev, "request_irq failed: %d\n", ret); + return ret; + } + + /* Clocks */ + + ispif->nclocks = 0; + while (res->clock[ispif->nclocks]) + ispif->nclocks++; + + ispif->clock = devm_kcalloc(dev, + ispif->nclocks, sizeof(*ispif->clock), + GFP_KERNEL); + if (!ispif->clock) + return -ENOMEM; + + for (i = 0; i < ispif->nclocks; i++) { + struct camss_clock *clock = &ispif->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->freq = NULL; + clock->nfreqs = 0; + } + + ispif->nclocks_for_reset = 0; + while (res->clock_for_reset[ispif->nclocks_for_reset]) + ispif->nclocks_for_reset++; + + ispif->clock_for_reset = devm_kcalloc(dev, + ispif->nclocks_for_reset, + sizeof(*ispif->clock_for_reset), + GFP_KERNEL); + if (!ispif->clock_for_reset) + return -ENOMEM; + + for (i = 0; i < ispif->nclocks_for_reset; i++) { + struct camss_clock *clock = &ispif->clock_for_reset[i]; + + clock->clk = devm_clk_get(dev, res->clock_for_reset[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->freq = NULL; + clock->nfreqs = 0; + } + + for (i = 0; i < ARRAY_SIZE(ispif->line); i++) + ispif->line[i].id = i; + + mutex_init(&ispif->power_lock); + ispif->power_count = 0; + + mutex_init(&ispif->config_lock); + + init_completion(&ispif->reset_complete); + + return 0; +} + +/* + * ispif_get_intf - Get ISPIF interface to use by VFE line id + * @line_id: VFE line id that the ISPIF line is connected to + * + * Return ISPIF interface to use + */ +static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id) +{ + switch (line_id) { + case (VFE_LINE_RDI0): + return RDI0; + case (VFE_LINE_RDI1): + return RDI1; + case (VFE_LINE_RDI2): + return RDI2; + case (VFE_LINE_PIX): + return PIX0; + default: + return RDI0; + } +} + +/* + * ispif_link_setup - Setup ISPIF connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad + * @remote: Pointer to remote pad + * @flags: Link flags + * + * Return 0 on success + */ +static int ispif_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (media_entity_remote_pad(local)) + return -EBUSY; + + if (local->flags & MEDIA_PAD_FL_SINK) { + struct v4l2_subdev *sd; + struct ispif_line *line; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + + msm_csid_get_csid_id(remote->entity, &line->csid_id); + } else { /* MEDIA_PAD_FL_SOURCE */ + struct v4l2_subdev *sd; + struct ispif_line *line; + enum vfe_line_id id; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + + msm_vfe_get_vfe_id(remote->entity, &line->vfe_id); + msm_vfe_get_vfe_line_id(remote->entity, &id); + line->interface = ispif_get_intf(id); + } + } + + return 0; +} + +static const struct v4l2_subdev_core_ops ispif_core_ops = { + .s_power = ispif_set_power, +}; + +static const struct v4l2_subdev_video_ops ispif_video_ops = { + .s_stream = ispif_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ispif_pad_ops = { + .enum_mbus_code = ispif_enum_mbus_code, + .enum_frame_size = ispif_enum_frame_size, + .get_fmt = ispif_get_format, + .set_fmt = ispif_set_format, +}; + +static const struct v4l2_subdev_ops ispif_v4l2_ops = { + .core = &ispif_core_ops, + .video = &ispif_video_ops, + .pad = &ispif_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ispif_v4l2_internal_ops = { + .open = ispif_init_formats, +}; + +static const struct media_entity_operations ispif_media_ops = { + .link_setup = ispif_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * msm_ispif_register_entities - Register subdev node for ISPIF module + * @ispif: ISPIF device + * @v4l2_dev: V4L2 device + * + * Return 0 on success or a negative error code otherwise + */ +int msm_ispif_register_entities(struct ispif_device *ispif, + struct v4l2_device *v4l2_dev) +{ + struct device *dev = to_device(ispif); + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + struct v4l2_subdev *sd = &ispif->line[i].subdev; + struct media_pad *pads = ispif->line[i].pads; + + v4l2_subdev_init(sd, &ispif_v4l2_ops); + sd->internal_ops = &ispif_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + MSM_ISPIF_NAME, i); + v4l2_set_subdevdata(sd, &ispif->line[i]); + + ret = ispif_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto error; + } + + pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.ops = &ispif_media_ops; + ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, + pads); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto error; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + media_entity_cleanup(&sd->entity); + goto error; + } + } + + return 0; + +error: + for (i--; i >= 0; i--) { + struct v4l2_subdev *sd = &ispif->line[i].subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } + + return ret; +} + +/* + * msm_ispif_unregister_entities - Unregister ISPIF module subdev node + * @ispif: ISPIF device + */ +void msm_ispif_unregister_entities(struct ispif_device *ispif) +{ + int i; + + mutex_destroy(&ispif->power_lock); + mutex_destroy(&ispif->config_lock); + + for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + struct v4l2_subdev *sd = &ispif->line[i].subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } +} diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h new file mode 100644 index 000000000000..c90e1598943a --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -0,0 +1,85 @@ +/* + * camss-ispif.h + * + * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module + * + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_ISPIF_H +#define QC_MSM_CAMSS_ISPIF_H + +#include +#include +#include +#include + +/* Number of ISPIF lines - same as number of CSID hardware modules */ +#define MSM_ISPIF_LINE_NUM 2 + +#define MSM_ISPIF_PAD_SINK 0 +#define MSM_ISPIF_PAD_SRC 1 +#define MSM_ISPIF_PADS_NUM 2 + +#define MSM_ISPIF_VFE_NUM 1 + +enum ispif_intf { + PIX0, + RDI0, + PIX1, + RDI1, + RDI2 +}; + +struct ispif_intf_cmd_reg { + u32 cmd_0; + u32 cmd_1; +}; + +struct ispif_line { + u8 id; + u8 csid_id; + u8 vfe_id; + enum ispif_intf interface; + struct v4l2_subdev subdev; + struct media_pad pads[MSM_ISPIF_PADS_NUM]; + struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM]; +}; + +struct ispif_device { + void __iomem *base; + void __iomem *base_clk_mux; + u32 irq; + char irq_name[30]; + struct camss_clock *clock; + int nclocks; + struct camss_clock *clock_for_reset; + int nclocks_for_reset; + struct completion reset_complete; + int power_count; + struct mutex power_lock; + struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM]; + struct mutex config_lock; + struct ispif_line line[MSM_ISPIF_LINE_NUM]; +}; + +struct resources_ispif; + +int msm_ispif_subdev_init(struct ispif_device *ispif, + const struct resources_ispif *res); + +int msm_ispif_register_entities(struct ispif_device *ispif, + struct v4l2_device *v4l2_dev); + +void msm_ispif_unregister_entities(struct ispif_device *ispif); + +#endif /* QC_MSM_CAMSS_ISPIF_H */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c new file mode 100644 index 000000000000..380b90bdd720 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -0,0 +1,3093 @@ +/* + * camss-vfe.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-vfe.h" +#include "camss.h" + +#define MSM_VFE_NAME "msm_vfe" + +#define vfe_line_array(ptr_line) \ + ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) + +#define to_vfe(ptr_line) \ + container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line) + +#define VFE_0_HW_VERSION 0x000 + +#define VFE_0_GLOBAL_RESET_CMD 0x00c +#define VFE_0_GLOBAL_RESET_CMD_CORE (1 << 0) +#define VFE_0_GLOBAL_RESET_CMD_CAMIF (1 << 1) +#define VFE_0_GLOBAL_RESET_CMD_BUS (1 << 2) +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG (1 << 3) +#define VFE_0_GLOBAL_RESET_CMD_REGISTER (1 << 4) +#define VFE_0_GLOBAL_RESET_CMD_TIMER (1 << 5) +#define VFE_0_GLOBAL_RESET_CMD_PM (1 << 6) +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR (1 << 7) +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN (1 << 8) + +#define VFE_0_MODULE_CFG 0x018 +#define VFE_0_MODULE_CFG_DEMUX (1 << 2) +#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE (1 << 3) +#define VFE_0_MODULE_CFG_SCALE_ENC (1 << 23) +#define VFE_0_MODULE_CFG_CROP_ENC (1 << 27) + +#define VFE_0_CORE_CFG 0x01c +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 + +#define VFE_0_IRQ_CMD 0x024 +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR (1 << 0) + +#define VFE_0_IRQ_MASK_0 0x028 +#define VFE_0_IRQ_MASK_0_CAMIF_SOF (1 << 0) +#define VFE_0_IRQ_MASK_0_CAMIF_EOF (1 << 1) +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) +#define VFE_0_IRQ_MASK_0_RESET_ACK (1 << 31) +#define VFE_0_IRQ_MASK_1 0x02c +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR (1 << 0) +#define VFE_0_IRQ_MASK_1_VIOLATION (1 << 7) +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK (1 << 8) +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) (1 << ((n) + 9)) +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) (1 << ((n) + 29)) + +#define VFE_0_IRQ_CLEAR_0 0x030 +#define VFE_0_IRQ_CLEAR_1 0x034 + +#define VFE_0_IRQ_STATUS_0 0x038 +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF (1 << 0) +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) +#define VFE_0_IRQ_STATUS_0_RESET_ACK (1 << 31) +#define VFE_0_IRQ_STATUS_1 0x03c +#define VFE_0_IRQ_STATUS_1_VIOLATION (1 << 7) +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK (1 << 8) +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) (1 << ((n) + 29)) + +#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 +#define VFE_0_VIOLATION_STATUS 0x48 + +#define VFE_0_BUS_CMD 0x4c +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) (1 << (x)) + +#define VFE_0_BUS_CFG 0x050 + +#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN (1 << 1) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1F << 2) + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ + (0x088 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ + (0x08c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff + +#define VFE_0_BUS_PING_PONG_STATUS 0x268 + +#define VFE_0_BUS_BDG_CMD 0x2c0 +#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 + +#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 +#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 +#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 +#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc +#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 +#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 +#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc +#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 +#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 + +#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) +#define VFE_0_RDI_CFG_x_RDI_EN_BIT (1 << 2) +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 +#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) (1 << (16 + (r))) + +#define VFE_0_CAMIF_CMD 0x2f4 +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2) +#define VFE_0_CAMIF_CFG 0x2f8 +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6) +#define VFE_0_CAMIF_FRAME_CFG 0x300 +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 +#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 +#define VFE_0_CAMIF_STATUS 0x31c +#define VFE_0_CAMIF_STATUS_HALT (1 << 31) + +#define VFE_0_REG_UPDATE 0x378 +#define VFE_0_REG_UPDATE_RDIn(n) (1 << (1 + (n))) +#define VFE_0_REG_UPDATE_line_n(n) \ + ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) + +#define VFE_0_DEMUX_CFG 0x424 +#define VFE_0_DEMUX_CFG_PERIOD 0x3 +#define VFE_0_DEMUX_GAIN_0 0x428 +#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) +#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) +#define VFE_0_DEMUX_GAIN_1 0x42c +#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) +#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) +#define VFE_0_DEMUX_EVEN_CFG 0x438 +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 +#define VFE_0_DEMUX_ODD_CFG 0x43c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 + +#define VFE_0_SCALE_ENC_Y_CFG 0x75c +#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 +#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 +#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c +#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 +#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c +#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 +#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 + +#define VFE_0_CROP_ENC_Y_WIDTH 0x854 +#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 +#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c +#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 + +#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 +#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) +#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 +#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) + +#define VFE_0_CGC_OVERRIDE_1 0x974 +#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) (1 << (x)) + +/* VFE reset timeout */ +#define VFE_RESET_TIMEOUT_MS 50 +/* VFE halt timeout */ +#define VFE_HALT_TIMEOUT_MS 100 +/* Max number of frame drop updates per frame */ +#define VFE_FRAME_DROP_UPDATES 5 +/* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */ +#define VFE_FRAME_DROP_VAL 20 + +#define VFE_NEXT_SOF_MS 500 + +#define CAMIF_TIMEOUT_SLEEP_US 1000 +#define CAMIF_TIMEOUT_ALL_US 1000000 + +#define SCALER_RATIO_MAX 16 + +static const struct { + u32 code; + u8 bpp; +} vfe_formats[] = { + { + MEDIA_BUS_FMT_UYVY8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_VYUY8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_YUYV8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_YVYU8_2X8, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + 10, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + 12, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + 12, + } +}; + +/* + * vfe_get_bpp - map media bus format to bits per pixel + * @code: media bus format code + * + * Return number of bits per pixel + */ +static u8 vfe_get_bpp(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) + if (code == vfe_formats[i].code) + return vfe_formats[i].bpp; + + WARN(1, "Unknown format\n"); + + return vfe_formats[0].bpp; +} + +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits & ~clr_bits, vfe->base + reg); +} + +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits | set_bits, vfe->base + reg); +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | + VFE_0_GLOBAL_RESET_CMD_BUS_MISR | + VFE_0_GLOBAL_RESET_CMD_PM | + VFE_0_GLOBAL_RESET_CMD_TIMER | + VFE_0_GLOBAL_RESET_CMD_REGISTER | + VFE_0_GLOBAL_RESET_CMD_BUS_BDG | + VFE_0_GLOBAL_RESET_CMD_BUS | + VFE_0_GLOBAL_RESET_CMD_CAMIF | + VFE_0_GLOBAL_RESET_CMD_CORE; + + writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); +} + +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line) +{ + int val = 0; + + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CALC_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CALC_WORD(pixel_per_line, 2, 8); + break; + } + + return val; +} + +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, + u16 *width, u16 *height, u16 *bytesperline) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + if (plane == 1) + *height /= 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + break; + } +} + +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable) +{ + u32 reg; + + if (enable) { + u16 width = 0, height = 0, bytesperline = 0, wpl; + + vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); + + wpl = vfe_word_per_line(pix->pixelformat, width); + + reg = height - 1; + reg |= ((wpl + 1) / 2 - 1) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + + wpl = vfe_word_per_line(pix->pixelformat, bytesperline); + + reg = 0x3; + reg |= (height - 1) << 4; + reg |= wpl << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } else { + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } +} + +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); + + reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); + + reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) + & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; + + writel_relaxed(reg, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); +} + +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, + u32 pattern) +{ + writel_relaxed(pattern, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); +} + +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset, + u16 depth) +{ + u32 reg; + + reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | + depth; + writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); +} + +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) +{ + wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + wmb(); +} + +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); +} + +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); +} + +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); + + return (reg >> wm) & 0x1; +} + +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) +{ + if (enable) + writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); + else + writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); +} + +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & + VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) +{ + writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, + vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); +} + +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, + u8 enable) +{ + struct vfe_line *line = container_of(output, struct vfe_line, output); + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (i == 0) { + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + } else if (i == 1) { + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + } else { + /* On current devices output->wm_num is always <= 2 */ + break; + } + + if (output->wm_idx[i] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + } +} + +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) +{ + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), + VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); + + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), + cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); + wmb(); + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + wmb(); +} + +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | + VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | + VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } +} + +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable) +{ + struct vfe_output *output = &vfe->line[line_id].output; + unsigned int i; + u32 irq_en0; + u32 irq_en1; + u32 comp_mask = 0; + + irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; + irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; + irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); + irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; + for (i = 0; i < output->wm_num; i++) { + irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( + output->wm_idx[i]); + comp_mask |= (1 << output->wm_idx[i]) << comp * 8; + } + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; + u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | + VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; + + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); +} + +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val, even_cfg, odd_cfg; + + writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); + + val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); + + val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; + break; + } + + writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); + writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); +} + +static inline u8 vfe_calc_interp_reso(u16 input, u16 output) +{ + if (input / output >= 16) + return 0; + + if (input / output >= 8) + return 1; + + if (input / output >= 4) + return 2; + + return 3; +} + +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 input, output; + u8 interp_reso; + u32 phase_mult; + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) + output = line->compose.height / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); +} + +static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 first, last; + + first = line->crop.left; + last = line->crop.left + line->crop.width - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); + + first = line->crop.left / 2; + last = line->crop.left / 2 + line->crop.width / 2 - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { + first = line->crop.top / 2; + last = line->crop.top / 2 + line->crop.height / 2 - 1; + } + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); +} + +static void vfe_set_clamp_cfg(struct vfe_device *vfe) +{ + u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | + VFE_0_CLAMP_ENC_MAX_CFG_CH1 | + VFE_0_CLAMP_ENC_MAX_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); + + val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | + VFE_0_CLAMP_ENC_MIN_CFG_CH1 | + VFE_0_CLAMP_ENC_MIN_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); +} + +/* + * vfe_reset - Trigger reset on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_reset(struct vfe_device *vfe) +{ + unsigned long time; + + reinit_completion(&vfe->reset_complete); + + vfe_global_reset(vfe); + + time = wait_for_completion_timeout(&vfe->reset_complete, + msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(to_device(vfe), "VFE reset timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * vfe_halt - Trigger halt on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_halt(struct vfe_device *vfe) +{ + unsigned long time; + + reinit_completion(&vfe->halt_complete); + + writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, + vfe->base + VFE_0_BUS_BDG_CMD); + + time = wait_for_completion_timeout(&vfe->halt_complete, + msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); + if (!time) { + dev_err(to_device(vfe), "VFE halt timeout\n"); + return -EIO; + } + + return 0; +} + +static void vfe_init_outputs(struct vfe_device *vfe) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + struct vfe_output *output = &vfe->line[i].output; + + output->state = VFE_OUTPUT_OFF; + output->buf[0] = NULL; + output->buf[1] = NULL; + INIT_LIST_HEAD(&output->pending_bufs); + + output->wm_num = 1; + if (vfe->line[i].id == VFE_LINE_PIX) + output->wm_num = 2; + } +} + +static void vfe_reset_output_maps(struct vfe_device *vfe) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) + vfe->wm_output_map[i] = VFE_LINE_NONE; +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) +{ + u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); + + if (enable) + vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); + else + vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); + + wmb(); +} + +static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) +{ + u32 val = VFE_0_MODULE_CFG_DEMUX | + VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | + VFE_0_MODULE_CFG_SCALE_ENC | + VFE_0_MODULE_CFG_CROP_ENC; + + if (enable) + writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); + else + writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); +} + +static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val; + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; + break; + } + + writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2; + val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].height - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); + + val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); + + val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); +} + +static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd) +{ + writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS, + vfe->base + VFE_0_CAMIF_CMD); + + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); +} + +static int vfe_camif_wait_for_stop(struct vfe_device *vfe) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, + val, + (val & VFE_0_CAMIF_STATUS_HALT), + CAMIF_TIMEOUT_SLEEP_US, + CAMIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__); + + return ret; +} + +static void vfe_output_init_addrs(struct vfe_device *vfe, + struct vfe_output *output, u8 sync) +{ + u32 ping_addr; + u32 pong_addr; + unsigned int i; + + output->active_buf = 0; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[0]) + ping_addr = output->buf[0]->addr[i]; + else + ping_addr = 0; + + if (output->buf[1]) + pong_addr = output->buf[1]->addr[i]; + else + pong_addr = ping_addr; + + vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); + vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); + if (sync) + vfe_bus_reload_wm(vfe, output->wm_idx[i]); + } +} + +static void vfe_output_update_ping_addr(struct vfe_device *vfe, + struct vfe_output *output, u8 sync) +{ + u32 addr; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[0]) + addr = output->buf[0]->addr[i]; + else + addr = 0; + + vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr); + if (sync) + vfe_bus_reload_wm(vfe, output->wm_idx[i]); + } +} + +static void vfe_output_update_pong_addr(struct vfe_device *vfe, + struct vfe_output *output, u8 sync) +{ + u32 addr; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[1]) + addr = output->buf[1]->addr[i]; + else + addr = 0; + + vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr); + if (sync) + vfe_bus_reload_wm(vfe, output->wm_idx[i]); + } + +} + +static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + int ret = -EBUSY; + int i; + + for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) { + if (vfe->wm_output_map[i] == VFE_LINE_NONE) { + vfe->wm_output_map[i] = line_id; + ret = i; + break; + } + } + + return ret; +} + +static int vfe_release_wm(struct vfe_device *vfe, u8 wm) +{ + if (wm >= ARRAY_SIZE(vfe->wm_output_map)) + return -EINVAL; + + vfe->wm_output_map[wm] = VFE_LINE_NONE; + + return 0; +} + +static void vfe_output_frame_drop(struct vfe_device *vfe, + struct vfe_output *output, + u32 drop_pattern) +{ + u8 drop_period; + unsigned int i; + + /* We need to toggle update period to be valid on next frame */ + output->drop_update_idx++; + output->drop_update_idx %= VFE_FRAME_DROP_UPDATES; + drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; + + for (i = 0; i < output->wm_num; i++) { + vfe_wm_set_framedrop_period(vfe, output->wm_idx[i], + drop_period); + vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i], + drop_pattern); + } + vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id); +} + +static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) +{ + struct camss_buffer *buffer = NULL; + + if (!list_empty(&output->pending_bufs)) { + buffer = list_first_entry(&output->pending_bufs, + struct camss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +/* + * vfe_buf_add_pending - Add output buffer to list of pending + * @output: VFE output + * @buffer: Video buffer + */ +static void vfe_buf_add_pending(struct vfe_output *output, + struct camss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->pending_bufs); +} + +/* + * vfe_buf_flush_pending - Flush all pending buffers. + * @output: VFE output + * @state: vb2 buffer state + */ +static void vfe_buf_flush_pending(struct vfe_output *output, + enum vb2_buffer_state state) +{ + struct camss_buffer *buf; + struct camss_buffer *t; + + list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } +} + +static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, + struct vfe_output *output) +{ + switch (output->state) { + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3); + break; + case VFE_OUTPUT_SINGLE: + default: + dev_err_ratelimited(to_device(vfe), + "Next buf in wrong state! %d\n", + output->state); + break; + } +} + +static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, + struct vfe_output *output) +{ + switch (output->state) { + case VFE_OUTPUT_CONTINUOUS: + output->state = VFE_OUTPUT_SINGLE; + vfe_output_frame_drop(vfe, output, 1); + break; + case VFE_OUTPUT_SINGLE: + output->state = VFE_OUTPUT_STOPPING; + vfe_output_frame_drop(vfe, output, 0); + break; + default: + dev_err_ratelimited(to_device(vfe), + "Last buff in wrong state! %d\n", + output->state); + break; + } +} + +static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, + struct vfe_output *output, + struct camss_buffer *new_buf) +{ + int inactive_idx; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + inactive_idx = !output->active_buf; + + if (!output->buf[inactive_idx]) { + output->buf[inactive_idx] = new_buf; + + if (inactive_idx) + vfe_output_update_pong_addr(vfe, output, 0); + else + vfe_output_update_ping_addr(vfe, output, 0); + + vfe_output_frame_drop(vfe, output, 3); + output->state = VFE_OUTPUT_CONTINUOUS; + } else { + vfe_buf_add_pending(output, new_buf); + dev_err_ratelimited(to_device(vfe), + "Inactive buffer is busy\n"); + } + break; + + case VFE_OUTPUT_IDLE: + if (!output->buf[0]) { + output->buf[0] = new_buf; + + vfe_output_init_addrs(vfe, output, 1); + + vfe_output_frame_drop(vfe, output, 1); + output->state = VFE_OUTPUT_SINGLE; + } else { + vfe_buf_add_pending(output, new_buf); + dev_err_ratelimited(to_device(vfe), + "Output idle with buffer set!\n"); + } + break; + + case VFE_OUTPUT_CONTINUOUS: + default: + vfe_buf_add_pending(output, new_buf); + break; + } +} + +static int vfe_get_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output; + unsigned long flags; + int i; + int wm_idx; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output = &line->output; + if (output->state != VFE_OUTPUT_OFF) { + dev_err(to_device(vfe), "Output is running\n"); + goto error; + } + output->state = VFE_OUTPUT_RESERVED; + + output->active_buf = 0; + + for (i = 0; i < output->wm_num; i++) { + wm_idx = vfe_reserve_wm(vfe, line->id); + if (wm_idx < 0) { + dev_err(to_device(vfe), "Can not reserve wm\n"); + goto error_get_wm; + } + output->wm_idx[i] = wm_idx; + } + + output->drop_update_idx = 0; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; + +error_get_wm: + for (i--; i >= 0; i--) + vfe_release_wm(vfe, output->wm_idx[i]); + output->state = VFE_OUTPUT_OFF; +error: + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return -EINVAL; +} + +static int vfe_put_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&vfe->output_lock, flags); + + for (i = 0; i < output->wm_num; i++) + vfe_release_wm(vfe, output->wm_idx[i]); + + output->state = VFE_OUTPUT_OFF; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + return 0; +} + +static int vfe_enable_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned int i; + u16 ub_size; + + switch (vfe->id) { + case 0: + ub_size = MSM_VFE_VFE0_UB_SIZE_RDI; + break; + case 1: + ub_size = MSM_VFE_VFE1_UB_SIZE_RDI; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&vfe->output_lock, flags); + + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id); + + if (output->state != VFE_OUTPUT_RESERVED) { + dev_err(to_device(vfe), "Output is not in reserved state %d\n", + output->state); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return -EINVAL; + } + output->state = VFE_OUTPUT_IDLE; + + output->buf[0] = vfe_buf_get_pending(output); + output->buf[1] = vfe_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VFE_OUTPUT_SINGLE; + + if (output->buf[1]) + output->state = VFE_OUTPUT_CONTINUOUS; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + vfe_output_frame_drop(vfe, output, 1); + break; + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3); + break; + default: + vfe_output_frame_drop(vfe, output, 0); + break; + } + + output->sequence = 0; + output->wait_sof = 0; + output->wait_reg_update = 0; + reinit_completion(&output->sof); + reinit_completion(&output->reg_update); + + vfe_output_init_addrs(vfe, output, 0); + + if (line->id != VFE_LINE_PIX) { + vfe_set_cgc_override(vfe, output->wm_idx[0], 1); + vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); + vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); + vfe_wm_set_subsample(vfe, output->wm_idx[0]); + vfe_set_rdi_cid(vfe, line->id, 0); + vfe_wm_set_ub_cfg(vfe, output->wm_idx[0], + (ub_size + 1) * output->wm_idx[0], ub_size); + vfe_wm_frame_based(vfe, output->wm_idx[0], 1); + vfe_wm_enable(vfe, output->wm_idx[0], 1); + vfe_bus_reload_wm(vfe, output->wm_idx[0]); + } else { + ub_size /= output->wm_num; + for (i = 0; i < output->wm_num; i++) { + vfe_set_cgc_override(vfe, output->wm_idx[i], 1); + vfe_wm_set_subsample(vfe, output->wm_idx[i]); + vfe_wm_set_ub_cfg(vfe, output->wm_idx[i], + (ub_size + 1) * output->wm_idx[i], + ub_size); + vfe_wm_line_based(vfe, output->wm_idx[i], + &line->video_out.active_fmt.fmt.pix_mp, + i, 1); + vfe_wm_enable(vfe, output->wm_idx[i], 1); + vfe_bus_reload_wm(vfe, output->wm_idx[i]); + } + vfe_enable_irq_pix_line(vfe, 0, line->id, 1); + vfe_set_module_cfg(vfe, 1); + vfe_set_camif_cfg(vfe, line); + vfe_set_xbar_cfg(vfe, output, 1); + vfe_set_demux_cfg(vfe, line); + vfe_set_scale_cfg(vfe, line); + vfe_set_crop_cfg(vfe, line); + vfe_set_clamp_cfg(vfe); + vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY); + } + + vfe_reg_update(vfe, line->id); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +static int vfe_disable_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned long time; + unsigned int i; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output->wait_sof = 1; + spin_unlock_irqrestore(&vfe->output_lock, flags); + + time = wait_for_completion_timeout(&output->sof, + msecs_to_jiffies(VFE_NEXT_SOF_MS)); + if (!time) + dev_err(to_device(vfe), "VFE sof timeout\n"); + + spin_lock_irqsave(&vfe->output_lock, flags); + for (i = 0; i < output->wm_num; i++) + vfe_wm_enable(vfe, output->wm_idx[i], 0); + + vfe_reg_update(vfe, line->id); + output->wait_reg_update = 1; + spin_unlock_irqrestore(&vfe->output_lock, flags); + + time = wait_for_completion_timeout(&output->reg_update, + msecs_to_jiffies(VFE_NEXT_SOF_MS)); + if (!time) + dev_err(to_device(vfe), "VFE reg update timeout\n"); + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (line->id != VFE_LINE_PIX) { + vfe_wm_frame_based(vfe, output->wm_idx[0], 0); + vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id); + vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); + vfe_set_cgc_override(vfe, output->wm_idx[0], 0); + spin_unlock_irqrestore(&vfe->output_lock, flags); + } else { + for (i = 0; i < output->wm_num; i++) { + vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); + vfe_set_cgc_override(vfe, output->wm_idx[i], 0); + } + + vfe_enable_irq_pix_line(vfe, 0, line->id, 0); + vfe_set_module_cfg(vfe, 0); + vfe_set_xbar_cfg(vfe, output, 0); + + vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY); + spin_unlock_irqrestore(&vfe->output_lock, flags); + + vfe_camif_wait_for_stop(vfe); + } + + return 0; +} + +/* + * vfe_enable - Enable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_enable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + int ret; + + mutex_lock(&vfe->stream_lock); + + if (!vfe->stream_count) { + vfe_enable_irq_common(vfe); + + vfe_bus_enable_wr_if(vfe, 1); + + vfe_set_qos(vfe); + } + + vfe->stream_count++; + + mutex_unlock(&vfe->stream_lock); + + ret = vfe_get_output(line); + if (ret < 0) + goto error_get_output; + + ret = vfe_enable_output(line); + if (ret < 0) + goto error_enable_output; + + vfe->was_streaming = 1; + + return 0; + + +error_enable_output: + vfe_put_output(line); + +error_get_output: + mutex_lock(&vfe->stream_lock); + + if (vfe->stream_count == 1) + vfe_bus_enable_wr_if(vfe, 0); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return ret; +} + +/* + * vfe_disable - Disable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_disable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + + vfe_disable_output(line); + + vfe_put_output(line); + + mutex_lock(&vfe->stream_lock); + + if (vfe->stream_count == 1) + vfe_bus_enable_wr_if(vfe, 0); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return 0; +} + +/* + * vfe_isr_sof - Process start of frame interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + output = &vfe->line[line_id].output; + if (output->wait_sof) { + output->wait_sof = 0; + complete(&output->sof); + } + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_reg_update - Process reg update interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); + + output = &vfe->line[line_id].output; + + if (output->wait_reg_update) { + output->wait_reg_update = 0; + complete(&output->reg_update); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return; + } + + if (output->state == VFE_OUTPUT_STOPPING) { + /* Release last buffer when hw is idle */ + if (output->last_buffer) { + vb2_buffer_done(&output->last_buffer->vb.vb2_buf, + VB2_BUF_STATE_DONE); + output->last_buffer = NULL; + } + output->state = VFE_OUTPUT_IDLE; + + /* Buffers received in stopping state are queued in */ + /* dma pending queue, start next capture here */ + + output->buf[0] = vfe_buf_get_pending(output); + output->buf[1] = vfe_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VFE_OUTPUT_SINGLE; + + if (output->buf[1]) + output->state = VFE_OUTPUT_CONTINUOUS; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + vfe_output_frame_drop(vfe, output, 2); + break; + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3); + break; + default: + vfe_output_frame_drop(vfe, output, 0); + break; + } + + vfe_output_init_addrs(vfe, output, 1); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_wm_done - Process write master done interrupt + * @vfe: VFE Device + * @wm: Write master id + */ +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) +{ + struct camss_buffer *ready_buf; + struct vfe_output *output; + dma_addr_t *new_addr; + unsigned long flags; + u32 active_index; + u64 ts = ktime_get_ns(); + unsigned int i; + + active_index = vfe_wm_get_ping_pong_status(vfe, wm); + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { + dev_err_ratelimited(to_device(vfe), + "Received wm done for unmapped index\n"); + goto out_unlock; + } + output = &vfe->line[vfe->wm_output_map[wm]].output; + + if (output->active_buf == active_index) { + dev_err_ratelimited(to_device(vfe), + "Active buffer mismatch!\n"); + goto out_unlock; + } + output->active_buf = active_index; + + ready_buf = output->buf[!active_index]; + if (!ready_buf) { + dev_err_ratelimited(to_device(vfe), + "Missing ready buf %d %d!\n", + !active_index, output->state); + goto out_unlock; + } + + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + /* Get next buffer */ + output->buf[!active_index] = vfe_buf_get_pending(output); + if (!output->buf[!active_index]) { + /* No next buffer - set same address */ + new_addr = ready_buf->addr; + vfe_buf_update_wm_on_last(vfe, output); + } else { + new_addr = output->buf[!active_index]->addr; + vfe_buf_update_wm_on_next(vfe, output); + } + + if (active_index) + for (i = 0; i < output->wm_num; i++) + vfe_wm_set_ping_addr(vfe, output->wm_idx[i], + new_addr[i]); + else + for (i = 0; i < output->wm_num; i++) + vfe_wm_set_pong_addr(vfe, output->wm_idx[i], + new_addr[i]); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + if (output->state == VFE_OUTPUT_STOPPING) + output->last_buffer = ready_buf; + else + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + return; + +out_unlock: + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_wm_done - Process composite image done interrupt + * @vfe: VFE Device + * @comp: Composite image id + */ +static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) + if (vfe->wm_output_map[i] == VFE_LINE_PIX) { + vfe_isr_wm_done(vfe, i); + break; + } +} + +/* + * vfe_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 value0, value1; + u32 violation; + int i, j; + + value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); + value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); + + writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0); + writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1); + + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); + + if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) + complete(&vfe->reset_complete); + + if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) { + violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); + dev_err_ratelimited(to_device(vfe), + "VFE: violation = 0x%08x\n", violation); + } + + if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) { + complete(&vfe->halt_complete); + writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); + } + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) + if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) + vfe_isr_reg_update(vfe, i); + + if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) + vfe_isr_sof(vfe, VFE_LINE_PIX); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) + vfe_isr_sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { + vfe_isr_comp_done(vfe, i); + for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) + if (vfe->wm_output_map[j] == VFE_LINE_PIX) + value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); + } + + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) + vfe_isr_wm_done(vfe, i); + + return IRQ_HANDLED; +} + +/* + * vfe_set_clock_rates - Calculate and set clock rates on VFE module + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_set_clock_rates(struct vfe_device *vfe) +{ + struct device *dev = to_device(vfe); + u32 pixel_clock[MSM_VFE_LINE_NUM]; + int i, j; + int ret; + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, + &pixel_clock[i]); + if (ret) + pixel_clock[i] = 0; + } + + for (i = 0; i < vfe->nclocks; i++) { + struct camss_clock *clock = &vfe->clock[i]; + + if (!strcmp(clock->name, "camss_vfe_vfe")) { + u64 min_rate = 0; + long rate; + + for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { + u32 tmp; + u8 bpp; + + if (j == VFE_LINE_PIX) { + tmp = pixel_clock[j]; + } else { + bpp = vfe_get_bpp(vfe->line[j]. + fmt[MSM_VFE_PAD_SINK].code); + tmp = pixel_clock[j] * bpp / 64; + } + + if (min_rate < tmp) + min_rate = tmp; + } + + camss_add_clock_margin(&min_rate); + + for (j = 0; j < clock->nfreqs; j++) + if (min_rate < clock->freq[j]) + break; + + if (j == clock->nfreqs) { + dev_err(dev, + "Pixel clock is too high for VFE"); + return -EINVAL; + } + + /* if sensor pixel clock is not available */ + /* set highest possible VFE clock rate */ + if (min_rate == 0) + j = clock->nfreqs - 1; + + rate = clk_round_rate(clock->clk, clock->freq[j]); + if (rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + rate); + return -EINVAL; + } + + ret = clk_set_rate(clock->clk, rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +/* + * vfe_check_clock_rates - Check current clock rates on VFE module + * @vfe: VFE device + * + * Return 0 if current clock rates are suitable for a new pipeline + * or a negative error code otherwise + */ +static int vfe_check_clock_rates(struct vfe_device *vfe) +{ + u32 pixel_clock[MSM_VFE_LINE_NUM]; + int i, j; + int ret; + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, + &pixel_clock[i]); + if (ret) + pixel_clock[i] = 0; + } + + for (i = 0; i < vfe->nclocks; i++) { + struct camss_clock *clock = &vfe->clock[i]; + + if (!strcmp(clock->name, "camss_vfe_vfe")) { + u64 min_rate = 0; + unsigned long rate; + + for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { + u32 tmp; + u8 bpp; + + if (j == VFE_LINE_PIX) { + tmp = pixel_clock[j]; + } else { + bpp = vfe_get_bpp(vfe->line[j]. + fmt[MSM_VFE_PAD_SINK].code); + tmp = pixel_clock[j] * bpp / 64; + } + + if (min_rate < tmp) + min_rate = tmp; + } + + camss_add_clock_margin(&min_rate); + + rate = clk_get_rate(clock->clk); + if (rate < min_rate) + return -EBUSY; + } + } + + return 0; +} + +/* + * vfe_get - Power up and reset VFE module + * @vfe: VFE Device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_get(struct vfe_device *vfe) +{ + int ret; + + mutex_lock(&vfe->power_lock); + + if (vfe->power_count == 0) { + ret = vfe_set_clock_rates(vfe); + if (ret < 0) + goto error_clocks; + + ret = camss_enable_clocks(vfe->nclocks, vfe->clock, + to_device(vfe)); + if (ret < 0) + goto error_clocks; + + ret = vfe_reset(vfe); + if (ret < 0) + goto error_reset; + + vfe_reset_output_maps(vfe); + + vfe_init_outputs(vfe); + } else { + ret = vfe_check_clock_rates(vfe); + if (ret < 0) + goto error_clocks; + } + vfe->power_count++; + + mutex_unlock(&vfe->power_lock); + + return 0; + +error_reset: + camss_disable_clocks(vfe->nclocks, vfe->clock); + +error_clocks: + mutex_unlock(&vfe->power_lock); + + return ret; +} + +/* + * vfe_put - Power down VFE module + * @vfe: VFE Device + */ +static void vfe_put(struct vfe_device *vfe) +{ + mutex_lock(&vfe->power_lock); + + if (vfe->power_count == 0) { + dev_err(to_device(vfe), "vfe power off on power_count == 0\n"); + goto exit; + } else if (vfe->power_count == 1) { + if (vfe->was_streaming) { + vfe->was_streaming = 0; + vfe_halt(vfe); + } + camss_disable_clocks(vfe->nclocks, vfe->clock); + } + + vfe->power_count--; + +exit: + mutex_unlock(&vfe->power_lock); +} + +/* + * vfe_video_pad_to_line - Get pointer to VFE line by media pad + * @pad: Media pad + * + * Return pointer to vfe line structure + */ +static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) +{ + struct media_pad *vfe_pad; + struct v4l2_subdev *subdev; + + vfe_pad = media_entity_remote_pad(pad); + if (vfe_pad == NULL) + return NULL; + + subdev = media_entity_to_v4l2_subdev(vfe_pad->entity); + + return container_of(subdev, struct vfe_line, subdev); +} + +/* + * vfe_queue_buffer - Add empty buffer + * @vid: Video device structure + * @buf: Buffer to be enqueued + * + * Add an empty buffer - depending on the current number of buffers it will be + * put in pending buffer queue or directly given to the hardware to be filled. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_queue_buffer(struct camss_video *vid, + struct camss_buffer *buf) +{ + struct vfe_device *vfe = &vid->camss->vfe; + struct vfe_line *line; + struct vfe_output *output; + unsigned long flags; + + line = vfe_video_pad_to_line(&vid->pad); + if (!line) { + dev_err(to_device(vfe), "Can not queue buffer\n"); + return -1; + } + output = &line->output; + + spin_lock_irqsave(&vfe->output_lock, flags); + + vfe_buf_update_wm_on_new(vfe, output, buf); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +/* + * vfe_flush_buffers - Return all vb2 buffers + * @vid: Video device structure + * @state: vb2 buffer state of the returned buffers + * + * Return all buffers to vb2. This includes queued pending buffers (still + * unused) and any buffers given to the hardware but again still not used. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_flush_buffers(struct camss_video *vid, + enum vb2_buffer_state state) +{ + struct vfe_device *vfe = &vid->camss->vfe; + struct vfe_line *line; + struct vfe_output *output; + unsigned long flags; + + line = vfe_video_pad_to_line(&vid->pad); + if (!line) { + dev_err(to_device(vfe), "Can not flush buffers\n"); + return -1; + } + output = &line->output; + + spin_lock_irqsave(&vfe->output_lock, flags); + + vfe_buf_flush_pending(output, state); + + if (output->buf[0]) + vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); + + if (output->buf[1]) + vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); + + if (output->last_buffer) { + vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); + output->last_buffer = NULL; + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +/* + * vfe_set_power - Power on/off VFE module + * @sd: VFE V4L2 subdevice + * @on: Requested power state + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_set_power(struct v4l2_subdev *sd, int on) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct vfe_device *vfe = to_vfe(line); + int ret; + + if (on) { + u32 hw_version; + + ret = vfe_get(vfe); + if (ret < 0) + return ret; + + hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); + dev_dbg(to_device(vfe), + "VFE HW Version = 0x%08x\n", hw_version); + } else { + vfe_put(vfe); + } + + return 0; +} + +/* + * vfe_set_stream - Enable/disable streaming on VFE module + * @sd: VFE V4L2 subdevice + * @enable: Requested streaming state + * + * Main configuration of VFE module is triggered here. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct vfe_device *vfe = to_vfe(line); + int ret; + + if (enable) { + ret = vfe_enable(line); + if (ret < 0) + dev_err(to_device(vfe), + "Failed to enable vfe outputs\n"); + } else { + ret = vfe_disable(line); + if (ret < 0) + dev_err(to_device(vfe), + "Failed to disable vfe outputs\n"); + } + + return ret; +} + +/* + * __vfe_get_format - Get pointer to format structure + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @pad: pad from which format is requested + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE format structure + */ +static struct v4l2_mbus_framefmt * +__vfe_get_format(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); + + return &line->fmt[pad]; +} + +/* + * __vfe_get_compose - Get pointer to compose selection structure + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE compose rectangle structure + */ +static struct v4l2_rect * +__vfe_get_compose(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&line->subdev, cfg, + MSM_VFE_PAD_SINK); + + return &line->compose; +} + +/* + * __vfe_get_crop - Get pointer to crop selection structure + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @which: TRY or ACTIVE format + * + * Return pointer to TRY or ACTIVE crop rectangle structure + */ +static struct v4l2_rect * +__vfe_get_crop(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&line->subdev, cfg, + MSM_VFE_PAD_SRC); + + return &line->crop; +} + +/* + * vfe_try_format - Handle try format by pad subdev method + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @pad: pad on which format is requested + * @fmt: pointer to v4l2 format structure + * @which: wanted subdev format + */ +static void vfe_try_format(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + u32 code; + + switch (pad) { + case MSM_VFE_PAD_SINK: + /* Set format on sink pad */ + + for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) + if (fmt->code == vfe_formats[i].code) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(vfe_formats)) + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + break; + + case MSM_VFE_PAD_SRC: + /* Set and return a format same as sink pad */ + + code = fmt->code; + + *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, + which); + + if (line->id == VFE_LINE_PIX) { + struct v4l2_rect *rect; + + rect = __vfe_get_crop(line, cfg, which); + + fmt->width = rect->width; + fmt->height = rect->height; + + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + if (code == MEDIA_BUS_FMT_YUYV8_1_5X8) + fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8; + else + fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + if (code == MEDIA_BUS_FMT_YVYU8_1_5X8) + fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8; + else + fmt->code = MEDIA_BUS_FMT_YVYU8_2X8; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + if (code == MEDIA_BUS_FMT_UYVY8_1_5X8) + fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8; + else + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + if (code == MEDIA_BUS_FMT_VYUY8_1_5X8) + fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8; + else + fmt->code = MEDIA_BUS_FMT_VYUY8_2X8; + break; + } + } + + break; + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* + * vfe_try_compose - Handle try compose selection by pad subdev method + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @rect: pointer to v4l2 rect structure + * @which: wanted subdev format + */ +static void vfe_try_compose(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); + + if (rect->width > fmt->width) + rect->width = fmt->width; + + if (rect->height > fmt->height) + rect->height = fmt->height; + + if (fmt->width > rect->width * SCALER_RATIO_MAX) + rect->width = (fmt->width + SCALER_RATIO_MAX - 1) / + SCALER_RATIO_MAX; + + rect->width &= ~0x1; + + if (fmt->height > rect->height * SCALER_RATIO_MAX) + rect->height = (fmt->height + SCALER_RATIO_MAX - 1) / + SCALER_RATIO_MAX; + + if (rect->width < 16) + rect->width = 16; + + if (rect->height < 4) + rect->height = 4; +} + +/* + * vfe_try_crop - Handle try crop selection by pad subdev method + * @line: VFE line + * @cfg: V4L2 subdev pad configuration + * @rect: pointer to v4l2 rect structure + * @which: wanted subdev format + */ +static void vfe_try_crop(struct vfe_line *line, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_rect *compose; + + compose = __vfe_get_compose(line, cfg, which); + + if (rect->width > compose->width) + rect->width = compose->width; + + if (rect->width + rect->left > compose->width) + rect->left = compose->width - rect->width; + + if (rect->height > compose->height) + rect->height = compose->height; + + if (rect->height + rect->top > compose->height) + rect->top = compose->height - rect->height; + + /* wm in line based mode writes multiple of 16 horizontally */ + rect->left += (rect->width & 0xf) >> 1; + rect->width &= ~0xf; + + if (rect->width < 16) { + rect->left = 0; + rect->width = 16; + } + + if (rect->height < 4) { + rect->top = 0; + rect->height = 4; + } +} + +/* + * vfe_enum_mbus_code - Handle pixel format enumeration + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * return -EINVAL or zero on success + */ +static int vfe_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == MSM_VFE_PAD_SINK) { + if (code->index >= ARRAY_SIZE(vfe_formats)) + return -EINVAL; + + code->code = vfe_formats[code->index].code; + } else { + if (code->index > 0) + return -EINVAL; + + format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, + code->which); + + code->code = format->code; + } + + return 0; +} + +/* + * vfe_enum_frame_size - Handle frame size enumeration + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: pointer to v4l2_subdev_frame_size_enum structure + * + * Return -EINVAL or zero on success + */ +static int vfe_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + vfe_try_format(line, cfg, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + vfe_try_format(line, cfg, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * vfe_get_format - Handle get format by pads subdev method + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int vfe_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int vfe_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel); + +/* + * vfe_set_format - Handle set format by pads subdev method + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: pointer to v4l2 subdev format structure + * + * Return -EINVAL or zero on success + */ +static int vfe_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->pad == MSM_VFE_PAD_SINK) { + struct v4l2_subdev_selection sel = { 0 }; + int ret; + + /* Propagate the format from sink to source */ + format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC, + fmt->which); + + *format = fmt->format; + vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format, + fmt->which); + + if (line->id != VFE_LINE_PIX) + return 0; + + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = MSM_VFE_PAD_SINK; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + ret = vfe_set_selection(sd, cfg, &sel); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * vfe_get_selection - Handle get selection by pads subdev method + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @sel: pointer to v4l2 subdev selection structure + * + * Return -EINVAL or zero on success + */ +static int vfe_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + int ret; + + if (line->id != VFE_LINE_PIX) + return -EINVAL; + + if (sel->pad == MSM_VFE_PAD_SINK) + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + fmt.pad = sel->pad; + fmt.which = sel->which; + ret = vfe_get_format(sd, cfg, &fmt); + if (ret < 0) + return ret; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + break; + case V4L2_SEL_TGT_COMPOSE: + rect = __vfe_get_compose(line, cfg, sel->which); + if (rect == NULL) + return -EINVAL; + + sel->r = *rect; + break; + default: + return -EINVAL; + } + else if (sel->pad == MSM_VFE_PAD_SRC) + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + rect = __vfe_get_compose(line, cfg, sel->which); + if (rect == NULL) + return -EINVAL; + + sel->r.left = rect->left; + sel->r.top = rect->top; + sel->r.width = rect->width; + sel->r.height = rect->height; + break; + case V4L2_SEL_TGT_CROP: + rect = __vfe_get_crop(line, cfg, sel->which); + if (rect == NULL) + return -EINVAL; + + sel->r = *rect; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * vfe_set_selection - Handle set selection by pads subdev method + * @sd: VFE V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @sel: pointer to v4l2 subdev selection structure + * + * Return -EINVAL or zero on success + */ +static int vfe_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vfe_line *line = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + int ret; + + if (line->id != VFE_LINE_PIX) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_COMPOSE && + sel->pad == MSM_VFE_PAD_SINK) { + struct v4l2_subdev_selection crop = { 0 }; + + rect = __vfe_get_compose(line, cfg, sel->which); + if (rect == NULL) + return -EINVAL; + + vfe_try_compose(line, cfg, &sel->r, sel->which); + *rect = sel->r; + + /* Reset source crop selection */ + crop.which = sel->which; + crop.pad = MSM_VFE_PAD_SRC; + crop.target = V4L2_SEL_TGT_CROP; + crop.r = *rect; + ret = vfe_set_selection(sd, cfg, &crop); + } else if (sel->target == V4L2_SEL_TGT_CROP && + sel->pad == MSM_VFE_PAD_SRC) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = __vfe_get_crop(line, cfg, sel->which); + if (rect == NULL) + return -EINVAL; + + vfe_try_crop(line, cfg, &sel->r, sel->which); + *rect = sel->r; + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = MSM_VFE_PAD_SRC; + ret = vfe_get_format(sd, cfg, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = vfe_set_format(sd, cfg, &fmt); + } else { + ret = -EINVAL; + } + + return ret; +} + +/* + * vfe_init_formats - Initialize formats on all pads + * @sd: VFE V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_VFE_PAD_SINK, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 1920, + .height = 1080 + } + }; + + return vfe_set_format(sd, fh ? fh->pad : NULL, &format); +} + +/* + * msm_vfe_subdev_init - Initialize VFE device structure and resources + * @vfe: VFE device + * @res: VFE module resources table + * + * Return 0 on success or a negative error code otherwise + */ +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) +{ + struct device *dev = to_device(vfe); + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + struct camss *camss = to_camss(vfe); + int i, j; + int ret; + + /* Memory */ + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); + vfe->base = devm_ioremap_resource(dev, r); + if (IS_ERR(vfe->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(vfe->base); + } + + /* Interrupt */ + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + res->interrupt[0]); + if (!r) { + dev_err(dev, "missing IRQ\n"); + return -EINVAL; + } + + vfe->irq = r->start; + snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", + dev_name(dev), MSM_VFE_NAME, vfe->id); + ret = devm_request_irq(dev, vfe->irq, vfe_isr, + IRQF_TRIGGER_RISING, vfe->irq_name, vfe); + if (ret < 0) { + dev_err(dev, "request_irq failed: %d\n", ret); + return ret; + } + + /* Clocks */ + + vfe->nclocks = 0; + while (res->clock[vfe->nclocks]) + vfe->nclocks++; + + vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock), + GFP_KERNEL); + if (!vfe->clock) + return -ENOMEM; + + for (i = 0; i < vfe->nclocks; i++) { + struct camss_clock *clock = &vfe->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + mutex_init(&vfe->power_lock); + vfe->power_count = 0; + + mutex_init(&vfe->stream_lock); + vfe->stream_count = 0; + + spin_lock_init(&vfe->output_lock); + + vfe->id = 0; + vfe->reg_update = 0; + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + vfe->line[i].video_out.type = + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vfe->line[i].video_out.camss = camss; + vfe->line[i].id = i; + init_completion(&vfe->line[i].output.sof); + init_completion(&vfe->line[i].output.reg_update); + } + + init_completion(&vfe->reset_complete); + init_completion(&vfe->halt_complete); + + return 0; +} + +/* + * msm_vfe_get_vfe_id - Get VFE HW module id + * @entity: Pointer to VFE media entity structure + * @id: Return CSID HW module id here + */ +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id) +{ + struct v4l2_subdev *sd; + struct vfe_line *line; + struct vfe_device *vfe; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + vfe = to_vfe(line); + + *id = vfe->id; +} + +/* + * msm_vfe_get_vfe_line_id - Get VFE line id by media entity + * @entity: Pointer to VFE media entity structure + * @id: Return VFE line id here + */ +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id) +{ + struct v4l2_subdev *sd; + struct vfe_line *line; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + + *id = line->id; +} + +/* + * vfe_link_setup - Setup VFE connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad + * @remote: Pointer to remote pad + * @flags: Link flags + * + * Return 0 on success + */ +static int vfe_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_entity_remote_pad(local)) + return -EBUSY; + + return 0; +} + +static const struct v4l2_subdev_core_ops vfe_core_ops = { + .s_power = vfe_set_power, +}; + +static const struct v4l2_subdev_video_ops vfe_video_ops = { + .s_stream = vfe_set_stream, +}; + +static const struct v4l2_subdev_pad_ops vfe_pad_ops = { + .enum_mbus_code = vfe_enum_mbus_code, + .enum_frame_size = vfe_enum_frame_size, + .get_fmt = vfe_get_format, + .set_fmt = vfe_set_format, + .get_selection = vfe_get_selection, + .set_selection = vfe_set_selection, +}; + +static const struct v4l2_subdev_ops vfe_v4l2_ops = { + .core = &vfe_core_ops, + .video = &vfe_video_ops, + .pad = &vfe_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = { + .open = vfe_init_formats, +}; + +static const struct media_entity_operations vfe_media_ops = { + .link_setup = vfe_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct camss_video_ops camss_vfe_video_ops = { + .queue_buffer = vfe_queue_buffer, + .flush_buffers = vfe_flush_buffers, +}; + +void msm_vfe_stop_streaming(struct vfe_device *vfe) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vfe->line); i++) + msm_video_stop_streaming(&vfe->line[i].video_out); +} + +/* + * msm_vfe_register_entities - Register subdev node for VFE module + * @vfe: VFE device + * @v4l2_dev: V4L2 device + * + * Initialize and register a subdev node for the VFE module. Then + * call msm_video_register() to register the video device node which + * will be connected to this subdev node. Then actually create the + * media link between them. + * + * Return 0 on success or a negative error code otherwise + */ +int msm_vfe_register_entities(struct vfe_device *vfe, + struct v4l2_device *v4l2_dev) +{ + struct device *dev = to_device(vfe); + struct v4l2_subdev *sd; + struct media_pad *pads; + struct camss_video *video_out; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + char name[32]; + + sd = &vfe->line[i].subdev; + pads = vfe->line[i].pads; + video_out = &vfe->line[i].video_out; + + v4l2_subdev_init(sd, &vfe_v4l2_ops); + sd->internal_ops = &vfe_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (i == VFE_LINE_PIX) + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", + MSM_VFE_NAME, vfe->id, "pix"); + else + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d", + MSM_VFE_NAME, vfe->id, "rdi", i); + + v4l2_set_subdevdata(sd, &vfe->line[i]); + + ret = vfe_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto error_init; + } + + pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &vfe_media_ops; + ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM, + pads); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto error_init; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + goto error_reg_subdev; + } + + video_out->ops = &camss_vfe_video_ops; + video_out->bpl_alignment = 8; + video_out->line_based = 0; + if (i == VFE_LINE_PIX) { + video_out->bpl_alignment = 16; + video_out->line_based = 1; + } + snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", + MSM_VFE_NAME, vfe->id, "video", i); + ret = msm_video_register(video_out, v4l2_dev, name, + i == VFE_LINE_PIX ? 1 : 0); + if (ret < 0) { + dev_err(dev, "Failed to register video node: %d\n", + ret); + goto error_reg_video; + } + + ret = media_create_pad_link( + &sd->entity, MSM_VFE_PAD_SRC, + &video_out->vdev.entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + dev_err(dev, "Failed to link %s->%s entities: %d\n", + sd->entity.name, video_out->vdev.entity.name, + ret); + goto error_link; + } + } + + return 0; + +error_link: + msm_video_unregister(video_out); + +error_reg_video: + v4l2_device_unregister_subdev(sd); + +error_reg_subdev: + media_entity_cleanup(&sd->entity); + +error_init: + for (i--; i >= 0; i--) { + sd = &vfe->line[i].subdev; + video_out = &vfe->line[i].video_out; + + msm_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } + + return ret; +} + +/* + * msm_vfe_unregister_entities - Unregister VFE module subdev node + * @vfe: VFE device + */ +void msm_vfe_unregister_entities(struct vfe_device *vfe) +{ + int i; + + mutex_destroy(&vfe->power_lock); + mutex_destroy(&vfe->stream_lock); + + for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + struct v4l2_subdev *sd = &vfe->line[i].subdev; + struct camss_video *video_out = &vfe->line[i].video_out; + + msm_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } +} diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h new file mode 100644 index 000000000000..5aa740741de8 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -0,0 +1,123 @@ +/* + * camss-vfe.h + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_VFE_H +#define QC_MSM_CAMSS_VFE_H + +#include +#include +#include +#include +#include + +#include "camss-video.h" + +#define MSM_VFE_PAD_SINK 0 +#define MSM_VFE_PAD_SRC 1 +#define MSM_VFE_PADS_NUM 2 + +#define MSM_VFE_LINE_NUM 4 +#define MSM_VFE_IMAGE_MASTERS_NUM 7 +#define MSM_VFE_COMPOSITE_IRQ_NUM 4 + +#define MSM_VFE_VFE0_UB_SIZE 1023 +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) +#define MSM_VFE_VFE1_UB_SIZE 1535 +#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) + +enum vfe_output_state { + VFE_OUTPUT_OFF, + VFE_OUTPUT_RESERVED, + VFE_OUTPUT_SINGLE, + VFE_OUTPUT_CONTINUOUS, + VFE_OUTPUT_IDLE, + VFE_OUTPUT_STOPPING +}; + +enum vfe_line_id { + VFE_LINE_NONE = -1, + VFE_LINE_RDI0 = 0, + VFE_LINE_RDI1 = 1, + VFE_LINE_RDI2 = 2, + VFE_LINE_PIX = 3 +}; + +struct vfe_output { + u8 wm_num; + u8 wm_idx[3]; + + int active_buf; + struct camss_buffer *buf[2]; + struct camss_buffer *last_buffer; + struct list_head pending_bufs; + + unsigned int drop_update_idx; + + enum vfe_output_state state; + unsigned int sequence; + int wait_sof; + int wait_reg_update; + struct completion sof; + struct completion reg_update; +}; + +struct vfe_line { + enum vfe_line_id id; + struct v4l2_subdev subdev; + struct media_pad pads[MSM_VFE_PADS_NUM]; + struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM]; + struct v4l2_rect compose; + struct v4l2_rect crop; + struct camss_video video_out; + struct vfe_output output; +}; + +struct vfe_device { + u8 id; + void __iomem *base; + u32 irq; + char irq_name[30]; + struct camss_clock *clock; + int nclocks; + struct completion reset_complete; + struct completion halt_complete; + struct mutex power_lock; + int power_count; + struct mutex stream_lock; + int stream_count; + spinlock_t output_lock; + enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; + struct vfe_line line[MSM_VFE_LINE_NUM]; + u32 reg_update; + u8 was_streaming; +}; + +struct resources; + +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res); + +int msm_vfe_register_entities(struct vfe_device *vfe, + struct v4l2_device *v4l2_dev); + +void msm_vfe_unregister_entities(struct vfe_device *vfe); + +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); + +void msm_vfe_stop_streaming(struct vfe_device *vfe); + +#endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c new file mode 100644 index 000000000000..0e7b842854ec --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -0,0 +1,859 @@ +/* + * camss-video.c + * + * Qualcomm MSM Camera Subsystem - V4L2 device node + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "camss-video.h" +#include "camss.h" + +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct camss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct camss_format_info { + u32 code; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + unsigned int bpp[3]; +}; + +static const struct camss_format_info formats_rdi[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, +}; + +static const struct camss_format_info formats_pix[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, +}; + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static int video_find_format(u32 code, u32 pixelformat, + const struct camss_format_info *formats, + unsigned int nformats) +{ + int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) + if (formats[i].code == code) + return i; + + WARN_ON(1); + + return -EINVAL; +} + +/* + * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane + * @mbus: v4l2_mbus_framefmt format (input) + * @pix: v4l2_pix_format_mplane format (output) + * @f: a pointer to formats array element to be used for the conversion + * @alignment: bytesperline alignment value + * + * Fill the output pix structure with information from the input mbus format. + * + * Return 0 on success or a negative error code otherwise + */ +static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format_mplane *pix, + const struct camss_format_info *f, + unsigned int alignment) +{ + unsigned int i; + u32 bytesperline; + + memset(pix, 0, sizeof(*pix)); + v4l2_fill_pix_format_mplane(pix, mbus); + pix->pixelformat = f->pixelformat; + pix->num_planes = f->planes; + for (i = 0; i < pix->num_planes; i++) { + bytesperline = pix->width / f->hsub[i].numerator * + f->hsub[i].denominator * f->bpp[i] / 8; + bytesperline = ALIGN(bytesperline, alignment); + pix->plane_fmt[i].bytesperline = bytesperline; + pix->plane_fmt[i].sizeimage = pix->height / + f->vsub[i].numerator * f->vsub[i].denominator * + bytesperline; + } + + return 0; +} + +static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, + u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(&video->pad); + + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int video_get_subdev_format(struct camss_video *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + subdev = video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EPIPE; + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) + return ret; + + ret = video_find_format(fmt.format.code, + format->fmt.pix_mp.pixelformat, + video->formats, video->nformats); + if (ret < 0) + return ret; + + format->type = video->type; + + return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, + &video->formats[ret], video->bpl_alignment); +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int video_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct camss_video *video = vb2_get_drv_priv(q); + const struct v4l2_pix_format_mplane *format = + &video->active_fmt.fmt.pix_mp; + unsigned int i; + + if (*num_planes) { + if (*num_planes != format->num_planes) + return -EINVAL; + + for (i = 0; i < *num_planes; i++) + if (sizes[i] < format->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + *num_planes = format->num_planes; + + for (i = 0; i < *num_planes; i++) + sizes[i] = format->plane_fmt[i].sizeimage; + + return 0; +} + +static int video_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, + vb); + const struct v4l2_pix_format_mplane *format = + &video->active_fmt.fmt.pix_mp; + struct sg_table *sgt; + unsigned int i; + + for (i = 0; i < format->num_planes; i++) { + sgt = vb2_dma_sg_plane_desc(vb, i); + if (!sgt) + return -EFAULT; + + buffer->addr[i] = sg_dma_address(sgt->sgl); + } + + if (format->pixelformat == V4L2_PIX_FMT_NV12 || + format->pixelformat == V4L2_PIX_FMT_NV21 || + format->pixelformat == V4L2_PIX_FMT_NV16 || + format->pixelformat == V4L2_PIX_FMT_NV61) + buffer->addr[1] = buffer->addr[0] + + format->plane_fmt[0].bytesperline * + format->height; + + return 0; +} + +static int video_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); + const struct v4l2_pix_format_mplane *format = + &video->active_fmt.fmt.pix_mp; + unsigned int i; + + for (i = 0; i < format->num_planes; i++) { + if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i)) + return -EINVAL; + + vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage); + } + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void video_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, + vb); + + video->ops->queue_buffer(video, buffer); +} + +static int video_check_format(struct camss_video *video) +{ + struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp; + struct v4l2_format format; + struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp; + int ret; + + sd_pix->pixelformat = pix->pixelformat; + ret = video_get_subdev_format(video, &format); + if (ret < 0) + return ret; + + if (pix->pixelformat != sd_pix->pixelformat || + pix->height != sd_pix->height || + pix->width != sd_pix->width || + pix->num_planes != sd_pix->num_planes || + pix->field != format.fmt.pix_mp.field) + return -EPIPE; + + return 0; +} + +static int video_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct camss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + ret = media_pipeline_start(&vdev->entity, &video->pipe); + if (ret < 0) + return ret; + + ret = video_check_format(video); + if (ret < 0) + goto error; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } + + return 0; + +error: + media_pipeline_stop(&vdev->entity); + + video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void video_stop_streaming(struct vb2_queue *q) +{ + struct camss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + media_pipeline_stop(&vdev->entity); + + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops msm_video_vb2_q_ops = { + .queue_setup = video_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = video_buf_init, + .buf_prepare = video_buf_prepare, + .buf_queue = video_buf_queue, + .start_streaming = video_start_streaming, + .stop_streaming = video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct camss_video *video = video_drvdata(file); + + strlcpy(cap->driver, "qcom-camss", sizeof(cap->driver)); + strlcpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->camss->dev)); + + return 0; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct camss_video *video = video_drvdata(file); + int i, j, k; + + if (f->type != video->type) + return -EINVAL; + + if (f->index >= video->nformats) + return -EINVAL; + + /* find index "i" of "k"th unique pixelformat in formats array */ + k = -1; + for (i = 0; i < video->nformats; i++) { + for (j = 0; j < i; j++) { + if (video->formats[i].pixelformat == + video->formats[j].pixelformat) + break; + } + + if (j == i) + k++; + + if (k == f->index) + break; + } + + if (k < f->index) + return -EINVAL; + + f->pixelformat = video->formats[i].pixelformat; + + return 0; +} + +static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct camss_video *video = video_drvdata(file); + + *f = video->active_fmt; + + return 0; +} + +static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp; + const struct camss_format_info *fi; + struct v4l2_plane_pix_format *p; + u32 bytesperline[3] = { 0 }; + u32 sizeimage[3] = { 0 }; + u32 width, height; + u32 bpl, lines; + int i, j; + + pix_mp = &f->fmt.pix_mp; + + if (video->line_based) + for (i = 0; i < pix_mp->num_planes && i < 3; i++) { + p = &pix_mp->plane_fmt[i]; + bytesperline[i] = clamp_t(u32, p->bytesperline, + 1, 65528); + sizeimage[i] = clamp_t(u32, p->sizeimage, + bytesperline[i], + bytesperline[i] * 4096); + } + + for (j = 0; j < video->nformats; j++) + if (pix_mp->pixelformat == video->formats[j].pixelformat) + break; + + if (j == video->nformats) + j = 0; /* default format */ + + fi = &video->formats[j]; + width = pix_mp->width; + height = pix_mp->height; + + memset(pix_mp, 0, sizeof(*pix_mp)); + + pix_mp->pixelformat = fi->pixelformat; + pix_mp->width = clamp_t(u32, width, 1, 8191); + pix_mp->height = clamp_t(u32, height, 1, 8191); + pix_mp->num_planes = fi->planes; + for (i = 0; i < pix_mp->num_planes; i++) { + bpl = pix_mp->width / fi->hsub[i].numerator * + fi->hsub[i].denominator * fi->bpp[i] / 8; + bpl = ALIGN(bpl, video->bpl_alignment); + pix_mp->plane_fmt[i].bytesperline = bpl; + pix_mp->plane_fmt[i].sizeimage = pix_mp->height / + fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; + } + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + pix_mp->flags = 0; + pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace); + pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + pix_mp->colorspace, pix_mp->ycbcr_enc); + pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace); + + if (video->line_based) + for (i = 0; i < pix_mp->num_planes; i++) { + p = &pix_mp->plane_fmt[i]; + p->bytesperline = clamp_t(u32, p->bytesperline, + 1, 65528); + p->sizeimage = clamp_t(u32, p->sizeimage, + p->bytesperline, + p->bytesperline * 4096); + lines = p->sizeimage / p->bytesperline; + + if (p->bytesperline < bytesperline[i]) + p->bytesperline = ALIGN(bytesperline[i], 8); + + if (p->sizeimage < p->bytesperline * lines) + p->sizeimage = p->bytesperline * lines; + + if (p->sizeimage < sizeimage[i]) + p->sizeimage = sizeimage[i]; + } + + return 0; +} + +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct camss_video *video = video_drvdata(file); + + return __video_try_fmt(video, f); +} + +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct camss_video *video = video_drvdata(file); + int ret; + + if (vb2_is_busy(&video->vb2_q)) + return -EBUSY; + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + video->active_fmt = *f; + + return 0; +} + +static int video_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct camss_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret; + + mutex_lock(&video->lock); + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (vfh == NULL) { + ret = -ENOMEM; + goto error_alloc; + } + + v4l2_fh_init(vfh, vdev); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + ret = v4l2_pipeline_pm_use(&vdev->entity, 1); + if (ret < 0) { + dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", + ret); + goto error_pm_use; + } + + mutex_unlock(&video->lock); + + return 0; + +error_pm_use: + v4l2_fh_release(file); + +error_alloc: + mutex_unlock(&video->lock); + + return ret; +} + +static int video_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + vb2_fop_release(file); + + v4l2_pipeline_pm_use(&vdev->entity, 0); + + file->private_data = NULL; + + return 0; +} + +static const struct v4l2_file_operations msm_vid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = video_open, + .release = video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * CAMSS video core + */ + +static void msm_video_release(struct video_device *vdev) +{ + struct camss_video *video = video_get_drvdata(vdev); + + media_entity_cleanup(&vdev->entity); + + mutex_destroy(&video->q_lock); + mutex_destroy(&video->lock); + + if (atomic_dec_and_test(&video->camss->ref_count)) + camss_delete(video->camss); +} + +/* + * msm_video_init_format - Helper function to initialize format + * @video: struct camss_video + * + * Initialize pad format with default value. + * + * Return 0 on success or a negative error code otherwise + */ +static int msm_video_init_format(struct camss_video *video) +{ + int ret; + struct v4l2_format format = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .fmt.pix_mp = { + .width = 1920, + .height = 1080, + .pixelformat = video->formats[0].pixelformat, + }, + }; + + ret = __video_try_fmt(video, &format); + if (ret < 0) + return ret; + + video->active_fmt = format; + + return 0; +} + +/* + * msm_video_register - Register a video device node + * @video: struct camss_video + * @v4l2_dev: V4L2 device + * @name: name to be used for the video device node + * + * Initialize and register a video device node to a V4L2 device. Also + * initialize the vb2 queue. + * + * Return 0 on success or a negative error code otherwise + */ + +int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, + const char *name, int is_pix) +{ + struct media_pad *pad = &video->pad; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + vdev = &video->vdev; + + mutex_init(&video->q_lock); + + q = &video->vb2_q; + q->drv_priv = video; + q->mem_ops = &vb2_dma_sg_memops; + q->ops = &msm_video_vb2_q_ops; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct camss_buffer); + q->dev = video->camss->dev; + q->lock = &video->q_lock; + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); + goto error_vb2_init; + } + + pad->flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, pad); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", + ret); + goto error_media_init; + } + + mutex_init(&video->lock); + + video->formats = formats_rdi; + video->nformats = ARRAY_SIZE(formats_rdi); + if (is_pix) { + video->formats = formats_pix; + video->nformats = ARRAY_SIZE(formats_pix); + } + + ret = msm_video_init_format(video); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); + goto error_video_register; + } + + vdev->fops = &msm_vid_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + vdev->ioctl_ops = &msm_vid_ioctl_ops; + vdev->release = msm_video_release; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = &video->vb2_q; + vdev->lock = &video->lock; + strlcpy(vdev->name, name, sizeof(vdev->name)); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", + ret); + goto error_video_register; + } + + video_set_drvdata(vdev, video); + atomic_inc(&video->camss->ref_count); + + return 0; + +error_video_register: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&video->lock); +error_media_init: + vb2_queue_release(&video->vb2_q); +error_vb2_init: + mutex_destroy(&video->q_lock); + + return ret; +} + +void msm_video_stop_streaming(struct camss_video *video) +{ + if (vb2_is_streaming(&video->vb2_q)) + vb2_queue_release(&video->vb2_q); +} + +void msm_video_unregister(struct camss_video *video) +{ + atomic_inc(&video->camss->ref_count); + video_unregister_device(&video->vdev); + atomic_dec(&video->camss->ref_count); +} diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h new file mode 100644 index 000000000000..821c1ef6b5c7 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -0,0 +1,70 @@ +/* + * camss-video.h + * + * Qualcomm MSM Camera Subsystem - V4L2 device node + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_VIDEO_H +#define QC_MSM_CAMSS_VIDEO_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct camss_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t addr[3]; + struct list_head queue; +}; + +struct camss_video; + +struct camss_video_ops { + int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf); + int (*flush_buffers)(struct camss_video *vid, + enum vb2_buffer_state state); +}; + +struct camss_format_info; + +struct camss_video { + struct camss *camss; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + struct v4l2_format active_fmt; + enum v4l2_buf_type type; + struct media_pipeline pipe; + const struct camss_video_ops *ops; + struct mutex lock; + struct mutex q_lock; + unsigned int bpl_alignment; + unsigned int line_based; + const struct camss_format_info *formats; + unsigned int nformats; +}; + +void msm_video_stop_streaming(struct camss_video *video); + +int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, + const char *name, int is_pix); + +void msm_video_unregister(struct camss_video *video); + +#endif /* QC_MSM_CAMSS_VIDEO_H */ diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c new file mode 100644 index 000000000000..d1d27fca2958 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss.c @@ -0,0 +1,751 @@ +/* + * camss.c + * + * Qualcomm MSM Camera Subsystem - Core + * + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "camss.h" + +#define CAMSS_CLOCK_MARGIN_NUMERATOR 105 +#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 + +static const struct resources csiphy_res[] = { + /* CSIPHY0 */ + { + .regulator = { NULL }, + .clock = { "camss_top_ahb", "ispif_ahb", + "camss_ahb", "csiphy0_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000 } }, + .reg = { "csiphy0", "csiphy0_clk_mux" }, + .interrupt = { "csiphy0" } + }, + + /* CSIPHY1 */ + { + .regulator = { NULL }, + .clock = { "camss_top_ahb", "ispif_ahb", + "camss_ahb", "csiphy1_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000 } }, + .reg = { "csiphy1", "csiphy1_clk_mux" }, + .interrupt = { "csiphy1" } + } +}; + +static const struct resources csid_res[] = { + /* CSID0 */ + { + .regulator = { "vdda" }, + .clock = { "camss_top_ahb", "ispif_ahb", + "csi0_ahb", "camss_ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + + /* CSID1 */ + { + .regulator = { "vdda" }, + .clock = { "camss_top_ahb", "ispif_ahb", + "csi1_ahb", "camss_ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, +}; + +static const struct resources_ispif ispif_res = { + /* ISPIF */ + .clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi" }, + .clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = "ispif" + +}; + +static const struct resources vfe_res = { + /* VFE0 */ + .regulator = { NULL }, + .clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe", + "iface", "bus", "camss_ahb" }, + .clock_rate = { { 0 }, + { 50000000, 80000000, 100000000, 160000000, + 177780000, 200000000, 266670000, 320000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } +}; + +/* + * camss_add_clock_margin - Add margin to clock frequency rate + * @rate: Clock frequency rate + * + * When making calculations with physical clock frequency values + * some safety margin must be added. Add it. + */ +inline void camss_add_clock_margin(u64 *rate) +{ + *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; + *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); +} + +/* + * camss_enable_clocks - Enable multiple clocks + * @nclocks: Number of clocks in clock array + * @clock: Clock array + * @dev: Device + * + * Return 0 on success or a negative error code otherwise + */ +int camss_enable_clocks(int nclocks, struct camss_clock *clock, + struct device *dev) +{ + int ret; + int i; + + for (i = 0; i < nclocks; i++) { + ret = clk_prepare_enable(clock[i].clk); + if (ret) { + dev_err(dev, "clock enable failed: %d\n", ret); + goto error; + } + } + + return 0; + +error: + for (i--; i >= 0; i--) + clk_disable_unprepare(clock[i].clk); + + return ret; +} + +/* + * camss_disable_clocks - Disable multiple clocks + * @nclocks: Number of clocks in clock array + * @clock: Clock array + */ +void camss_disable_clocks(int nclocks, struct camss_clock *clock) +{ + int i; + + for (i = nclocks - 1; i >= 0; i--) + clk_disable_unprepare(clock[i].clk); +} + +/* + * camss_find_sensor - Find a linked media entity which represents a sensor + * @entity: Media entity to start searching from + * + * Return a pointer to sensor media entity or NULL if not found + */ +static struct media_entity *camss_find_sensor(struct media_entity *entity) +{ + struct media_pad *pad; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return NULL; + + entity = pad->entity; + + if (entity->function == MEDIA_ENT_F_CAM_SENSOR) + return entity; + } +} + +/* + * camss_get_pixel_clock - Get pixel clock rate from sensor + * @entity: Media entity in the current pipeline + * @pixel_clock: Received pixel clock value + * + * Return 0 on success or a negative error code otherwise + */ +int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) +{ + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_ctrl *ctrl; + + sensor = camss_find_sensor(entity); + if (!sensor) + return -ENODEV; + + subdev = media_entity_to_v4l2_subdev(sensor); + + ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); + + if (!ctrl) + return -EINVAL; + + *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); + + return 0; +} + +/* + * camss_of_parse_endpoint_node - Parse port endpoint node + * @dev: Device + * @node: Device node to be parsed + * @csd: Parsed data from port endpoint node + * + * Return 0 on success or a negative error code on failure + */ +static int camss_of_parse_endpoint_node(struct device *dev, + struct device_node *node, + struct camss_async_subdev *csd) +{ + struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; + struct v4l2_fwnode_endpoint vep = { { 0 } }; + unsigned int i; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + + csd->interface.csiphy_id = vep.base.port; + + mipi_csi2 = &vep.bus.mipi_csi2; + lncfg->clk.pos = mipi_csi2->clock_lane; + lncfg->clk.pol = mipi_csi2->lane_polarities[0]; + lncfg->num_data = mipi_csi2->num_data_lanes; + + lncfg->data = devm_kcalloc(dev, + lncfg->num_data, sizeof(*lncfg->data), + GFP_KERNEL); + if (!lncfg->data) + return -ENOMEM; + + for (i = 0; i < lncfg->num_data; i++) { + lncfg->data[i].pos = mipi_csi2->data_lanes[i]; + lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; + } + + return 0; +} + +/* + * camss_of_parse_ports - Parse ports node + * @dev: Device + * @notifier: v4l2_device notifier data + * + * Return number of "port" nodes found in "ports" node + */ +static int camss_of_parse_ports(struct device *dev, + struct v4l2_async_notifier *notifier) +{ + struct device_node *node = NULL; + struct device_node *remote = NULL; + unsigned int size, i; + int ret; + + while ((node = of_graph_get_next_endpoint(dev->of_node, node))) + if (of_device_is_available(node)) + notifier->num_subdevs++; + + size = sizeof(*notifier->subdevs) * notifier->num_subdevs; + notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL); + if (!notifier->subdevs) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + i = 0; + while ((node = of_graph_get_next_endpoint(dev->of_node, node))) { + struct camss_async_subdev *csd; + + if (!of_device_is_available(node)) + continue; + + csd = devm_kzalloc(dev, sizeof(*csd), GFP_KERNEL); + if (!csd) { + of_node_put(node); + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + notifier->subdevs[i++] = &csd->asd; + + ret = camss_of_parse_endpoint_node(dev, node, csd); + if (ret < 0) { + of_node_put(node); + return ret; + } + + remote = of_graph_get_remote_port_parent(node); + of_node_put(node); + + if (!remote) { + dev_err(dev, "Cannot get remote parent\n"); + return -EINVAL; + } + + csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + csd->asd.match.fwnode = of_fwnode_handle(remote); + } + + return notifier->num_subdevs; +} + +/* + * camss_init_subdevices - Initialize subdev structures and resources + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_init_subdevices(struct camss *camss) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { + ret = msm_csiphy_subdev_init(&camss->csiphy[i], + &csiphy_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { + ret = msm_csid_subdev_init(&camss->csid[i], + &csid_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csid%d sub-device: %d\n", + i, ret); + return ret; + } + } + + ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res); + if (ret < 0) { + dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", + ret); + return ret; + } + + ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res); + if (ret < 0) { + dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * camss_register_entities - Register subdev nodes and create links + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_register_entities(struct camss *camss) +{ + int i, j; + int ret; + + for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { + ret = msm_csiphy_register_entity(&camss->csiphy[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csiphy%d entity: %d\n", + i, ret); + goto err_reg_csiphy; + } + } + + for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { + ret = msm_csid_register_entity(&camss->csid[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csid%d entity: %d\n", + i, ret); + goto err_reg_csid; + } + } + + ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, "Failed to register ispif entities: %d\n", + ret); + goto err_reg_ispif; + } + + ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, "Failed to register vfe entities: %d\n", + ret); + goto err_reg_vfe; + } + + for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { + for (j = 0; j < ARRAY_SIZE(camss->csid); j++) { + ret = media_create_pad_link( + &camss->csiphy[i].subdev.entity, + MSM_CSIPHY_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + camss->csiphy[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); + goto err_link; + } + } + } + + for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { + for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) { + ret = media_create_pad_link( + &camss->csid[i].subdev.entity, + MSM_CSID_PAD_SRC, + &camss->ispif.line[j].subdev.entity, + MSM_ISPIF_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + camss->csid[i].subdev.entity.name, + camss->ispif.line[j].subdev.entity.name, + ret); + goto err_link; + } + } + } + + for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) { + for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) { + ret = media_create_pad_link( + &camss->ispif.line[i].subdev.entity, + MSM_ISPIF_PAD_SRC, + &camss->vfe.line[j].subdev.entity, + MSM_VFE_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + camss->ispif.line[i].subdev.entity.name, + camss->vfe.line[j].subdev.entity.name, + ret); + goto err_link; + } + } + } + + return 0; + +err_link: + msm_vfe_unregister_entities(&camss->vfe); +err_reg_vfe: + msm_ispif_unregister_entities(&camss->ispif); +err_reg_ispif: + + i = ARRAY_SIZE(camss->csid); +err_reg_csid: + for (i--; i >= 0; i--) + msm_csid_unregister_entity(&camss->csid[i]); + + i = ARRAY_SIZE(camss->csiphy); +err_reg_csiphy: + for (i--; i >= 0; i--) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + + return ret; +} + +/* + * camss_unregister_entities - Unregister subdev nodes + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static void camss_unregister_entities(struct camss *camss) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + + for (i = 0; i < ARRAY_SIZE(camss->csid); i++) + msm_csid_unregister_entity(&camss->csid[i]); + + msm_ispif_unregister_entities(&camss->ispif); + msm_vfe_unregister_entities(&camss->vfe); +} + +static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct camss *camss = container_of(async, struct camss, notifier); + struct camss_async_subdev *csd = + container_of(asd, struct camss_async_subdev, asd); + u8 id = csd->interface.csiphy_id; + struct csiphy_device *csiphy = &camss->csiphy[id]; + + csiphy->cfg.csi2 = &csd->interface.csi2; + subdev->host_priv = csiphy; + + return 0; +} + +static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) +{ + struct camss *camss = container_of(async, struct camss, notifier); + struct v4l2_device *v4l2_dev = &camss->v4l2_dev; + struct v4l2_subdev *sd; + int ret; + + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (sd->host_priv) { + struct media_entity *sensor = &sd->entity; + struct csiphy_device *csiphy = + (struct csiphy_device *) sd->host_priv; + struct media_entity *input = &csiphy->subdev.entity; + unsigned int i; + + for (i = 0; i < sensor->num_pads; i++) { + if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + if (i == sensor->num_pads) { + dev_err(camss->dev, + "No source pad in external entity\n"); + return -EINVAL; + } + + ret = media_create_pad_link(sensor, i, + input, MSM_CSIPHY_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + sensor->name, input->name, ret); + return ret; + } + } + } + + ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); + if (ret < 0) + return ret; + + return media_device_register(&camss->media_dev); +} + +static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { + .bound = camss_subdev_notifier_bound, + .complete = camss_subdev_notifier_complete, +}; + +static const struct media_device_ops camss_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* + * camss_probe - Probe CAMSS platform device + * @pdev: Pointer to CAMSS platform device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct camss *camss; + int ret; + + camss = kzalloc(sizeof(*camss), GFP_KERNEL); + if (!camss) + return -ENOMEM; + + atomic_set(&camss->ref_count, 0); + camss->dev = dev; + platform_set_drvdata(pdev, camss); + + ret = camss_of_parse_ports(dev, &camss->notifier); + if (ret < 0) + return ret; + + ret = camss_init_subdevices(camss); + if (ret < 0) + return ret; + + ret = dma_set_mask_and_coherent(dev, 0xffffffff); + if (ret) + return ret; + + camss->media_dev.dev = camss->dev; + strlcpy(camss->media_dev.model, "Qualcomm Camera Subsystem", + sizeof(camss->media_dev.model)); + camss->media_dev.ops = &camss_media_ops; + media_device_init(&camss->media_dev); + + camss->v4l2_dev.mdev = &camss->media_dev; + ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); + if (ret < 0) { + dev_err(dev, "Failed to register V4L2 device: %d\n", ret); + return ret; + } + + ret = camss_register_entities(camss); + if (ret < 0) + goto err_register_entities; + + if (camss->notifier.num_subdevs) { + camss->notifier.ops = &camss_subdev_notifier_ops; + + ret = v4l2_async_notifier_register(&camss->v4l2_dev, + &camss->notifier); + if (ret) { + dev_err(dev, + "Failed to register async subdev nodes: %d\n", + ret); + goto err_register_subdevs; + } + } else { + ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); + if (ret < 0) { + dev_err(dev, "Failed to register subdev nodes: %d\n", + ret); + goto err_register_subdevs; + } + + ret = media_device_register(&camss->media_dev); + if (ret < 0) { + dev_err(dev, "Failed to register media device: %d\n", + ret); + goto err_register_subdevs; + } + } + + return 0; + +err_register_subdevs: + camss_unregister_entities(camss); +err_register_entities: + v4l2_device_unregister(&camss->v4l2_dev); + + return ret; +} + +void camss_delete(struct camss *camss) +{ + v4l2_device_unregister(&camss->v4l2_dev); + media_device_unregister(&camss->media_dev); + media_device_cleanup(&camss->media_dev); + + kfree(camss); +} + +/* + * camss_remove - Remove CAMSS platform device + * @pdev: Pointer to CAMSS platform device + * + * Always returns 0. + */ +static int camss_remove(struct platform_device *pdev) +{ + struct camss *camss = platform_get_drvdata(pdev); + + msm_vfe_stop_streaming(&camss->vfe); + + v4l2_async_notifier_unregister(&camss->notifier); + camss_unregister_entities(camss); + + if (atomic_read(&camss->ref_count) == 0) + camss_delete(camss); + + return 0; +} + +static const struct of_device_id camss_dt_match[] = { + { .compatible = "qcom,msm8916-camss" }, + { } +}; + +MODULE_DEVICE_TABLE(of, camss_dt_match); + +static struct platform_driver qcom_camss_driver = { + .probe = camss_probe, + .remove = camss_remove, + .driver = { + .name = "qcom-camss", + .of_match_table = camss_dt_match, + }, +}; + +module_platform_driver(qcom_camss_driver); + +MODULE_ALIAS("platform:qcom-camss"); +MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); +MODULE_AUTHOR("Todor Tomov "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h new file mode 100644 index 000000000000..0e7cfe6bfbe5 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss.h @@ -0,0 +1,106 @@ +/* + * camss.h + * + * Qualcomm MSM Camera Subsystem - Core + * + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef QC_MSM_CAMSS_H +#define QC_MSM_CAMSS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "camss-csid.h" +#include "camss-csiphy.h" +#include "camss-ispif.h" +#include "camss-vfe.h" + +#define CAMSS_CSID_NUM 2 +#define CAMSS_CSIPHY_NUM 2 + +#define to_camss(ptr_module) \ + container_of(ptr_module, struct camss, ptr_module) + +#define to_device(ptr_module) \ + (to_camss(ptr_module)->dev) + +#define module_pointer(ptr_module, index) \ + ((const struct ptr_module##_device (*)[]) &(ptr_module[-(index)])) + +#define to_camss_index(ptr_module, index) \ + container_of(module_pointer(ptr_module, index), \ + struct camss, ptr_module) + +#define to_device_index(ptr_module, index) \ + (to_camss_index(ptr_module, index)->dev) + +#define CAMSS_RES_MAX 15 + +struct resources { + char *regulator[CAMSS_RES_MAX]; + char *clock[CAMSS_RES_MAX]; + u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; + char *reg[CAMSS_RES_MAX]; + char *interrupt[CAMSS_RES_MAX]; +}; + +struct resources_ispif { + char *clock[CAMSS_RES_MAX]; + char *clock_for_reset[CAMSS_RES_MAX]; + char *reg[CAMSS_RES_MAX]; + char *interrupt; +}; + +struct camss { + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + struct media_device media_dev; + struct device *dev; + struct csiphy_device csiphy[CAMSS_CSIPHY_NUM]; + struct csid_device csid[CAMSS_CSID_NUM]; + struct ispif_device ispif; + struct vfe_device vfe; + atomic_t ref_count; +}; + +struct camss_camera_interface { + u8 csiphy_id; + struct csiphy_csi2_cfg csi2; +}; + +struct camss_async_subdev { + struct camss_camera_interface interface; + struct v4l2_async_subdev asd; +}; + +struct camss_clock { + struct clk *clk; + const char *name; + u32 *freq; + u32 nfreqs; +}; + +void camss_add_clock_margin(u64 *rate); +int camss_enable_clocks(int nclocks, struct camss_clock *clock, + struct device *dev); +void camss_disable_clocks(int nclocks, struct camss_clock *clock); +int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock); +void camss_delete(struct camss *camss); + +#endif /* QC_MSM_CAMSS_H */ -- cgit v1.2.3-70-g09d2 From b873663bd85fff5e43bcedb5cc5360f2aa992e05 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:14 -0400 Subject: media: camss: Use SPDX license headers Use SPDX license headers for all files of the Qualcomm CAMSS driver. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 10 +--------- drivers/media/platform/qcom/camss/camss-csid.h | 10 +--------- drivers/media/platform/qcom/camss/camss-csiphy.c | 10 +--------- drivers/media/platform/qcom/camss/camss-csiphy.h | 10 +--------- drivers/media/platform/qcom/camss/camss-ispif.c | 10 +--------- drivers/media/platform/qcom/camss/camss-ispif.h | 10 +--------- drivers/media/platform/qcom/camss/camss-vfe.c | 10 +--------- drivers/media/platform/qcom/camss/camss-vfe.h | 10 +--------- drivers/media/platform/qcom/camss/camss-video.c | 10 +--------- drivers/media/platform/qcom/camss/camss-video.h | 10 +--------- drivers/media/platform/qcom/camss/camss.c | 10 +--------- drivers/media/platform/qcom/camss/camss.h | 10 +--------- 12 files changed, 12 insertions(+), 108 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 39ea27b9da3b..c0fef1787ad4 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-csid.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 8012222b81ad..ae1d045844fb 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-csid.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_CSID_H #define QC_MSM_CAMSS_CSID_H diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 642de259dc6b..b37e69161712 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-csiphy.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2016-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 9a422095b773..76fa2395664c 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-csiphy.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2016-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_CSIPHY_H #define QC_MSM_CAMSS_CSIPHY_H diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 636d5e712037..5ad719d7e437 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-ispif.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index c90e1598943a..a5dfb4f27d0e 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-ispif.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_ISPIF_H #define QC_MSM_CAMSS_ISPIF_H diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 380b90bdd720..256dc2d42411 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-vfe.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 5aa740741de8..6b4258d8761e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-vfe.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_VFE_H #define QC_MSM_CAMSS_VFE_H diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 0e7b842854ec..16e74b2448fe 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-video.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index 821c1ef6b5c7..aa35e8cc6fd5 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-video.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_VIDEO_H #define QC_MSM_CAMSS_VIDEO_H diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index d1d27fca2958..45285eb6842c 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss.c * @@ -5,15 +6,6 @@ * * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 0e7cfe6bfbe5..fb1c2f946e71 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss.h * @@ -5,15 +6,6 @@ * * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015-2018 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QC_MSM_CAMSS_H #define QC_MSM_CAMSS_H -- cgit v1.2.3-70-g09d2 From 2004fc09b34128182ed26ec7b8f28190e5189061 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:15 -0400 Subject: media: camss: Fix OF node usage of_graph_get_next_endpoint increases the refcount of the returned node and decreases the refcount of the passed node. Take this into account and use of_node_put properly. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 45285eb6842c..abf618489187 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -296,6 +296,7 @@ static int camss_of_parse_ports(struct device *dev, if (of_device_is_available(node)) notifier->num_subdevs++; + of_node_put(node); size = sizeof(*notifier->subdevs) * notifier->num_subdevs; notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL); if (!notifier->subdevs) { @@ -326,16 +327,16 @@ static int camss_of_parse_ports(struct device *dev, } remote = of_graph_get_remote_port_parent(node); - of_node_put(node); - if (!remote) { dev_err(dev, "Cannot get remote parent\n"); + of_node_put(node); return -EINVAL; } csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; csd->asd.match.fwnode = of_fwnode_handle(remote); } + of_node_put(node); return notifier->num_subdevs; } -- cgit v1.2.3-70-g09d2 From 5ba913b3fca8579d43c24d220d2f579cf3e992af Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:16 -0400 Subject: media: camss: csiphy: Ensure clock mux config is done before the rest Add a write memory barier after clock mux config and before the rest of the csiphy config. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csiphy.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index b37e69161712..2a9adcd6ff3c 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -364,6 +364,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) val |= cfg->csid_id; } writel_relaxed(val, csiphy->base_clk_mux); + wmb(); writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); -- cgit v1.2.3-70-g09d2 From 09a94865d4691726656909236b5d60b4e9dcc798 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:18 -0400 Subject: media: camss: Unify the clock names Use more logical clock names - similar to the names in documentation. This will allow better handling of the clocks in the driver when support for more hardware versions is added - equivalent clocks on different hardware versions will have the same name. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index abf618489187..0b663e009f8f 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -32,8 +32,7 @@ static const struct resources csiphy_res[] = { /* CSIPHY0 */ { .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy0_timer" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, .clock_rate = { { 0 }, { 0 }, { 0 }, @@ -45,8 +44,7 @@ static const struct resources csiphy_res[] = { /* CSIPHY1 */ { .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy1_timer" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, .clock_rate = { { 0 }, { 0 }, { 0 }, @@ -60,8 +58,7 @@ static const struct resources csid_res[] = { /* CSID0 */ { .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi0_ahb", "camss_ahb", + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, { 0 }, @@ -78,8 +75,7 @@ static const struct resources csid_res[] = { /* CSID1 */ { .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi1_ahb", "camss_ahb", + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, { 0 }, @@ -96,10 +92,10 @@ static const struct resources csid_res[] = { static const struct resources_ispif ispif_res = { /* ISPIF */ - .clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb", + .clock = { "top_ahb", "ahb", "ispif_ahb", "csi0", "csi0_pix", "csi0_rdi", "csi1", "csi1_pix", "csi1_rdi" }, - .clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" }, + .clock_for_reset = { "vfe0", "csi_vfe0" }, .reg = { "ispif", "csi_clk_mux" }, .interrupt = "ispif" @@ -108,8 +104,8 @@ static const struct resources_ispif ispif_res = { static const struct resources vfe_res = { /* VFE0 */ .regulator = { NULL }, - .clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe", - "iface", "bus", "camss_ahb" }, + .clock = { "top_ahb", "vfe0", "csi_vfe0", + "vfe_ahb", "vfe_axi", "ahb" }, .clock_rate = { { 0 }, { 50000000, 80000000, 100000000, 160000000, 177780000, 200000000, 266670000, 320000000, -- cgit v1.2.3-70-g09d2 From 7066647cdb4d3ea24fe6f34a9fc9812711eacba6 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:19 -0400 Subject: media: camss: csiphy: Update settle count calculation Update settle count calculation as per specification. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csiphy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 2a9adcd6ff3c..6158ffd3b358 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -329,7 +329,7 @@ static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate); - settle_cnt = t_hs_settle / timer_period; + settle_cnt = t_hs_settle / timer_period - 1; return settle_cnt; } -- cgit v1.2.3-70-g09d2 From c628e78899ff8006b5f9d8206da54ed3bb994342 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:20 -0400 Subject: media: camss: csid: Configure data type and decode format properly The CSID decodes the input data stream. When the input comes from the Test Generator the format of the stream is set on the source media pad. When the input comes from the CSIPHY the format is the one on the sink media pad. Use the proper format for each case. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index c0fef1787ad4..3cde07e44af6 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -384,9 +384,6 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) return -ENOLINK; - dt = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SRC].code)-> - data_type; - if (tg->enabled) { /* Config Test Generator */ struct v4l2_mbus_framefmt *f = @@ -408,6 +405,9 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(0)); + dt = csid_get_fmt_entry( + csid->fmt[MSM_CSID_PAD_SRC].code)->data_type; + /* 5:0 data type */ val = dt; writel_relaxed(val, csid->base + @@ -417,6 +417,9 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) val = tg->payload_mode; writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0)); + + df = csid_get_fmt_entry( + csid->fmt[MSM_CSID_PAD_SRC].code)->decode_format; } else { struct csid_phy_config *phy = &csid->phy; @@ -431,13 +434,16 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); + + dt = csid_get_fmt_entry( + csid->fmt[MSM_CSID_PAD_SINK].code)->data_type; + df = csid_get_fmt_entry( + csid->fmt[MSM_CSID_PAD_SINK].code)->decode_format; } /* Config LUT */ dt_shift = (cid % 4) * 8; - df = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SINK].code)-> - decode_format; val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); val &= ~(0xff << dt_shift); -- cgit v1.2.3-70-g09d2 From c9896b655177892cfaed2c0910fd55f44772ecad Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:21 -0400 Subject: media: camss: vfe: Fix to_vfe() macro member name Use the member name which is "line" instead of the pointer argument. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 256dc2d42411..51ad3f8ce73d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -30,7 +30,7 @@ ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) #define to_vfe(ptr_line) \ - container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line) + container_of(vfe_line_array(ptr_line), struct vfe_device, line) #define VFE_0_HW_VERSION 0x000 -- cgit v1.2.3-70-g09d2 From a93e5f4fc8dfecd15df51ae176a77ce5b0db82b1 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:22 -0400 Subject: media: camss: vfe: Get line pointer as container of video_out Simplify getting of the line pointer by using the container_of macro instead of traversing media controller links. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 38 +++------------------------ 1 file changed, 4 insertions(+), 34 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 51ad3f8ce73d..77167f11b655 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2037,26 +2037,6 @@ exit: mutex_unlock(&vfe->power_lock); } -/* - * vfe_video_pad_to_line - Get pointer to VFE line by media pad - * @pad: Media pad - * - * Return pointer to vfe line structure - */ -static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) -{ - struct media_pad *vfe_pad; - struct v4l2_subdev *subdev; - - vfe_pad = media_entity_remote_pad(pad); - if (vfe_pad == NULL) - return NULL; - - subdev = media_entity_to_v4l2_subdev(vfe_pad->entity); - - return container_of(subdev, struct vfe_line, subdev); -} - /* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure @@ -2070,16 +2050,11 @@ static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf) { - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; + struct vfe_line *line = container_of(vid, struct vfe_line, video_out); + struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; unsigned long flags; - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not queue buffer\n"); - return -1; - } output = &line->output; spin_lock_irqsave(&vfe->output_lock, flags); @@ -2104,16 +2079,11 @@ static int vfe_queue_buffer(struct camss_video *vid, static int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state) { - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; + struct vfe_line *line = container_of(vid, struct vfe_line, video_out); + struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; unsigned long flags; - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not flush buffers\n"); - return -1; - } output = &line->output; spin_lock_irqsave(&vfe->output_lock, flags); -- cgit v1.2.3-70-g09d2 From 06d690f092db28a7c83c9524010c42136e2bb37f Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:23 -0400 Subject: media: camss: vfe: Do not disable CAMIF when clearing its status Use "no change" value when clearing CAMIF status and make sure this is done before configuring the new command. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 77167f11b655..15a1a0100ba8 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -156,6 +156,7 @@ #define VFE_0_CAMIF_CMD 0x2f4 #define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 #define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_NO_CHANGE 3 #define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2) #define VFE_0_CAMIF_CFG 0x2f8 #define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6) @@ -1021,8 +1022,10 @@ static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd) { - writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS, + writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | + VFE_0_CAMIF_CMD_NO_CHANGE, vfe->base + VFE_0_CAMIF_CMD); + wmb(); writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); } -- cgit v1.2.3-70-g09d2 From 9c3e59de6041f84294fbead39c85b5541ac7b361 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:26 -0400 Subject: media: camss: Add 8x96 resources Add structs with 8x96 resources. As the number of CSIPHY, CSID and VFE hardware modules is different on 8x16 and 8x96 select the number at runtime and allocate needed structures dynamically. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 20 +- drivers/media/platform/qcom/camss/camss-csid.h | 3 +- drivers/media/platform/qcom/camss/camss-csiphy.c | 19 +- drivers/media/platform/qcom/camss/camss-csiphy.h | 4 +- drivers/media/platform/qcom/camss/camss-ispif.c | 35 ++- drivers/media/platform/qcom/camss/camss-ispif.h | 9 +- drivers/media/platform/qcom/camss/camss-vfe.c | 61 ++-- drivers/media/platform/qcom/camss/camss-vfe.h | 4 +- drivers/media/platform/qcom/camss/camss.c | 354 ++++++++++++++++++----- drivers/media/platform/qcom/camss/camss.h | 20 +- 10 files changed, 390 insertions(+), 139 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 3cde07e44af6..627ef44cf895 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -219,7 +219,7 @@ static irqreturn_t csid_isr(int irq, void *dev) */ static int csid_set_clock_rates(struct csid_device *csid) { - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; u32 pixel_clock; int i, j; int ret; @@ -232,7 +232,9 @@ static int csid_set_clock_rates(struct csid_device *csid) struct camss_clock *clock = &csid->clock[i]; if (!strcmp(clock->name, "csi0") || - !strcmp(clock->name, "csi1")) { + !strcmp(clock->name, "csi1") || + !strcmp(clock->name, "csi2") || + !strcmp(clock->name, "csi3")) { u8 bpp = csid_get_fmt_entry( csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; u8 num_lanes = csid->phy.lane_cnt; @@ -291,8 +293,7 @@ static int csid_reset(struct csid_device *csid) time = wait_for_completion_timeout(&csid->reset_complete, msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device_index(csid, csid->id), - "CSID reset timeout\n"); + dev_err(csid->camss->dev, "CSID reset timeout\n"); return -EIO; } @@ -309,7 +310,7 @@ static int csid_reset(struct csid_device *csid) static int csid_set_power(struct v4l2_subdev *sd, int on) { struct csid_device *csid = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; int ret; if (on) { @@ -375,7 +376,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) ret = v4l2_ctrl_handler_setup(&csid->ctrls); if (ret < 0) { - dev_err(to_device_index(csid, csid->id), + dev_err(csid->camss->dev, "could not sync v4l2 controls: %d\n", ret); return ret; } @@ -796,15 +797,16 @@ static const struct v4l2_ctrl_ops csid_ctrl_ops = { * * Return 0 on success or a negative error code otherwise */ -int msm_csid_subdev_init(struct csid_device *csid, +int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct resources *res, u8 id) { - struct device *dev = to_device_index(csid, id); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; int i, j; int ret; + csid->camss = camss; csid->id = id; /* Memory */ @@ -1018,7 +1020,7 @@ int msm_csid_register_entity(struct csid_device *csid, { struct v4l2_subdev *sd = &csid->subdev; struct media_pad *pads = csid->pads; - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; int ret; v4l2_subdev_init(sd, &csid_v4l2_ops); diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index ae1d045844fb..ed605fd3bb87 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -42,6 +42,7 @@ struct csid_phy_config { }; struct csid_device { + struct camss *camss; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSID_PADS_NUM]; @@ -61,7 +62,7 @@ struct csid_device { struct resources; -int msm_csid_subdev_init(struct csid_device *csid, +int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct resources *res, u8 id); int msm_csid_register_entity(struct csid_device *csid, diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 6158ffd3b358..0383e94996a3 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -155,7 +155,7 @@ static irqreturn_t csiphy_isr(int irq, void *dev) */ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) { - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; u32 pixel_clock; int i, j; int ret; @@ -168,7 +168,8 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) struct camss_clock *clock = &csiphy->clock[i]; if (!strcmp(clock->name, "csiphy0_timer") || - !strcmp(clock->name, "csiphy1_timer")) { + !strcmp(clock->name, "csiphy1_timer") || + !strcmp(clock->name, "csiphy2_timer")) { u8 bpp = csiphy_get_bpp( csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -233,7 +234,7 @@ static void csiphy_reset(struct csiphy_device *csiphy) static int csiphy_set_power(struct v4l2_subdev *sd, int on) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; if (on) { u8 hw_version; @@ -311,12 +312,12 @@ static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); if (ret) { - dev_err(to_device_index(csiphy, csiphy->id), + dev_err(csiphy->camss->dev, "Cannot get CSI2 transmitter's pixel clock\n"); return 0; } if (!pixel_clock) { - dev_err(to_device_index(csiphy, csiphy->id), + dev_err(csiphy->camss->dev, "Got pixel clock == 0, cannot continue\n"); return 0; } @@ -670,15 +671,17 @@ static int csiphy_init_formats(struct v4l2_subdev *sd, * * Return 0 on success or a negative error code otherwise */ -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, const struct resources *res, u8 id) { - struct device *dev = to_device_index(csiphy, id); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; int i, j; int ret; + csiphy->camss = camss; csiphy->id = id; csiphy->cfg.combo_mode = 0; @@ -839,7 +842,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, { struct v4l2_subdev *sd = &csiphy->subdev; struct media_pad *pads = csiphy->pads; - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; int ret; v4l2_subdev_init(sd, &csiphy_v4l2_ops); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 76fa2395664c..728dfef18676 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -42,6 +42,7 @@ struct csiphy_config { }; struct csiphy_device { + struct camss *camss; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSIPHY_PADS_NUM]; @@ -58,7 +59,8 @@ struct csiphy_device { struct resources; -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, const struct resources *res, u8 id); int msm_csiphy_register_entity(struct csiphy_device *csiphy, diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 5ad719d7e437..ed50cc55e3d5 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -23,12 +23,6 @@ #define MSM_ISPIF_NAME "msm_ispif" -#define ispif_line_array(ptr_line) \ - ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)])) - -#define to_ispif(ptr_line) \ - container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line) - #define ISPIF_RST_CMD_0 0x008 #define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) #define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) @@ -225,7 +219,7 @@ static int ispif_reset(struct ispif_device *ispif) static int ispif_set_power(struct v4l2_subdev *sd, int on) { struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); + struct ispif_device *ispif = line->ispif; struct device *dev = to_device(ispif); int ret = 0; @@ -611,7 +605,7 @@ static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd, static int ispif_set_stream(struct v4l2_subdev *sd, int enable) { struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); + struct ispif_device *ispif = line->ispif; enum ispif_intf intf = line->interface; u8 csid = line->csid_id; u8 vfe = line->vfe_id; @@ -899,6 +893,24 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, int i; int ret; + /* Number of ISPIF lines - same as number of CSID hardware modules */ + if (to_camss(ispif)->version == CAMSS_8x16) + ispif->line_num = 2; + else if (to_camss(ispif)->version == CAMSS_8x96) + ispif->line_num = 4; + else + return -EINVAL; + + ispif->line = kcalloc(ispif->line_num, sizeof(*ispif->line), + GFP_KERNEL); + if (!ispif->line) + return -ENOMEM; + + for (i = 0; i < ispif->line_num; i++) { + ispif->line[i].ispif = ispif; + ispif->line[i].id = i; + } + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -979,9 +991,6 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, clock->nfreqs = 0; } - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) - ispif->line[i].id = i; - mutex_init(&ispif->power_lock); ispif->power_count = 0; @@ -1100,7 +1109,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, int ret; int i; - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + for (i = 0; i < ispif->line_num; i++) { struct v4l2_subdev *sd = &ispif->line[i].subdev; struct media_pad *pads = ispif->line[i].pads; @@ -1161,7 +1170,7 @@ void msm_ispif_unregister_entities(struct ispif_device *ispif) mutex_destroy(&ispif->power_lock); mutex_destroy(&ispif->config_lock); - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + for (i = 0; i < ispif->line_num; i++) { struct v4l2_subdev *sd = &ispif->line[i].subdev; v4l2_device_unregister_subdev(sd); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index a5dfb4f27d0e..9cd51dcb3ede 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -15,14 +15,11 @@ #include #include -/* Number of ISPIF lines - same as number of CSID hardware modules */ -#define MSM_ISPIF_LINE_NUM 2 - #define MSM_ISPIF_PAD_SINK 0 #define MSM_ISPIF_PAD_SRC 1 #define MSM_ISPIF_PADS_NUM 2 -#define MSM_ISPIF_VFE_NUM 1 +#define MSM_ISPIF_VFE_NUM 2 enum ispif_intf { PIX0, @@ -38,6 +35,7 @@ struct ispif_intf_cmd_reg { }; struct ispif_line { + struct ispif_device *ispif; u8 id; u8 csid_id; u8 vfe_id; @@ -61,7 +59,8 @@ struct ispif_device { struct mutex power_lock; struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM]; struct mutex config_lock; - struct ispif_line line[MSM_ISPIF_LINE_NUM]; + unsigned int line_num; + struct ispif_line *line; }; struct resources_ispif; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 15a1a0100ba8..3f589c448cd2 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -877,7 +877,7 @@ static int vfe_reset(struct vfe_device *vfe) time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device(vfe), "VFE reset timeout\n"); + dev_err(vfe->camss->dev, "VFE reset timeout\n"); return -EIO; } @@ -902,7 +902,7 @@ static int vfe_halt(struct vfe_device *vfe) time = wait_for_completion_timeout(&vfe->halt_complete, msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); if (!time) { - dev_err(to_device(vfe), "VFE halt timeout\n"); + dev_err(vfe->camss->dev, "VFE halt timeout\n"); return -EIO; } @@ -1041,7 +1041,7 @@ static int vfe_camif_wait_for_stop(struct vfe_device *vfe) CAMIF_TIMEOUT_SLEEP_US, CAMIF_TIMEOUT_ALL_US); if (ret < 0) - dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__); + dev_err(vfe->camss->dev, "%s: camif stop timeout\n", __func__); return ret; } @@ -1209,7 +1209,7 @@ static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, break; case VFE_OUTPUT_SINGLE: default: - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Next buf in wrong state! %d\n", output->state); break; @@ -1229,7 +1229,7 @@ static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, vfe_output_frame_drop(vfe, output, 0); break; default: - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Last buff in wrong state! %d\n", output->state); break; @@ -1258,7 +1258,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, output->state = VFE_OUTPUT_CONTINUOUS; } else { vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Inactive buffer is busy\n"); } break; @@ -1273,7 +1273,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, output->state = VFE_OUTPUT_SINGLE; } else { vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Output idle with buffer set!\n"); } break; @@ -1297,7 +1297,7 @@ static int vfe_get_output(struct vfe_line *line) output = &line->output; if (output->state != VFE_OUTPUT_OFF) { - dev_err(to_device(vfe), "Output is running\n"); + dev_err(vfe->camss->dev, "Output is running\n"); goto error; } output->state = VFE_OUTPUT_RESERVED; @@ -1307,7 +1307,7 @@ static int vfe_get_output(struct vfe_line *line) for (i = 0; i < output->wm_num; i++) { wm_idx = vfe_reserve_wm(vfe, line->id); if (wm_idx < 0) { - dev_err(to_device(vfe), "Can not reserve wm\n"); + dev_err(vfe->camss->dev, "Can not reserve wm\n"); goto error_get_wm; } output->wm_idx[i] = wm_idx; @@ -1371,7 +1371,7 @@ static int vfe_enable_output(struct vfe_line *line) vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id); if (output->state != VFE_OUTPUT_RESERVED) { - dev_err(to_device(vfe), "Output is not in reserved state %d\n", + dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); spin_unlock_irqrestore(&vfe->output_lock, flags); return -EINVAL; @@ -1471,7 +1471,7 @@ static int vfe_disable_output(struct vfe_line *line) time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS)); if (!time) - dev_err(to_device(vfe), "VFE sof timeout\n"); + dev_err(vfe->camss->dev, "VFE sof timeout\n"); spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) @@ -1484,7 +1484,7 @@ static int vfe_disable_output(struct vfe_line *line) time = wait_for_completion_timeout(&output->reg_update, msecs_to_jiffies(VFE_NEXT_SOF_MS)); if (!time) - dev_err(to_device(vfe), "VFE reg update timeout\n"); + dev_err(vfe->camss->dev, "VFE reg update timeout\n"); spin_lock_irqsave(&vfe->output_lock, flags); @@ -1698,14 +1698,14 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) spin_lock_irqsave(&vfe->output_lock, flags); if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Received wm done for unmapped index\n"); goto out_unlock; } output = &vfe->line[vfe->wm_output_map[wm]].output; if (output->active_buf == active_index) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Active buffer mismatch!\n"); goto out_unlock; } @@ -1713,7 +1713,7 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) ready_buf = output->buf[!active_index]; if (!ready_buf) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Missing ready buf %d %d!\n", !active_index, output->state); goto out_unlock; @@ -1799,7 +1799,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) { violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "VFE: violation = 0x%08x\n", violation); } @@ -1842,7 +1842,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) */ static int vfe_set_clock_rates(struct vfe_device *vfe) { - struct device *dev = to_device(vfe); + struct device *dev = vfe->camss->dev; u32 pixel_clock[MSM_VFE_LINE_NUM]; int i, j; int ret; @@ -1857,7 +1857,8 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) for (i = 0; i < vfe->nclocks; i++) { struct camss_clock *clock = &vfe->clock[i]; - if (!strcmp(clock->name, "camss_vfe_vfe")) { + if (!strcmp(clock->name, "vfe0") || + !strcmp(clock->name, "vfe1")) { u64 min_rate = 0; long rate; @@ -1935,7 +1936,8 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) for (i = 0; i < vfe->nclocks; i++) { struct camss_clock *clock = &vfe->clock[i]; - if (!strcmp(clock->name, "camss_vfe_vfe")) { + if (!strcmp(clock->name, "vfe0") || + !strcmp(clock->name, "vfe1")) { u64 min_rate = 0; unsigned long rate; @@ -1984,7 +1986,7 @@ static int vfe_get(struct vfe_device *vfe) goto error_clocks; ret = camss_enable_clocks(vfe->nclocks, vfe->clock, - to_device(vfe)); + vfe->camss->dev); if (ret < 0) goto error_clocks; @@ -2024,7 +2026,7 @@ static void vfe_put(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { - dev_err(to_device(vfe), "vfe power off on power_count == 0\n"); + dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n"); goto exit; } else if (vfe->power_count == 1) { if (vfe->was_streaming) { @@ -2130,7 +2132,7 @@ static int vfe_set_power(struct v4l2_subdev *sd, int on) return ret; hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); - dev_dbg(to_device(vfe), + dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version); } else { vfe_put(vfe); @@ -2157,12 +2159,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) if (enable) { ret = vfe_enable(line); if (ret < 0) - dev_err(to_device(vfe), + dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { ret = vfe_disable(line); if (ret < 0) - dev_err(to_device(vfe), + dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); } @@ -2716,12 +2718,12 @@ static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) * * Return 0 on success or a negative error code otherwise */ -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) +int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, + const struct resources *res, u8 id) { - struct device *dev = to_device(vfe); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; - struct camss *camss = to_camss(vfe); int i, j; int ret; @@ -2801,7 +2803,8 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) spin_lock_init(&vfe->output_lock); - vfe->id = 0; + vfe->camss = camss; + vfe->id = id; vfe->reg_update = 0; for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { @@ -2933,7 +2936,7 @@ void msm_vfe_stop_streaming(struct vfe_device *vfe) int msm_vfe_register_entities(struct vfe_device *vfe, struct v4l2_device *v4l2_dev) { - struct device *dev = to_device(vfe); + struct device *dev = vfe->camss->dev; struct v4l2_subdev *sd; struct media_pad *pads; struct camss_video *video_out; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 6b4258d8761e..17d431e563da 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -79,6 +79,7 @@ struct vfe_line { }; struct vfe_device { + struct camss *camss; u8 id; void __iomem *base; u32 irq; @@ -100,7 +101,8 @@ struct vfe_device { struct resources; -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res); +int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, + const struct resources *res, u8 id); int msm_vfe_register_entities(struct vfe_device *vfe, struct v4l2_device *v4l2_dev); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 0b663e009f8f..171e2c9d669e 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -28,7 +28,7 @@ #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 -static const struct resources csiphy_res[] = { +static const struct resources csiphy_res_8x16[] = { /* CSIPHY0 */ { .regulator = { NULL }, @@ -54,7 +54,7 @@ static const struct resources csiphy_res[] = { } }; -static const struct resources csid_res[] = { +static const struct resources csid_res_8x16[] = { /* CSID0 */ { .regulator = { "vdda" }, @@ -90,7 +90,7 @@ static const struct resources csid_res[] = { }, }; -static const struct resources_ispif ispif_res = { +static const struct resources_ispif ispif_res_8x16 = { /* ISPIF */ .clock = { "top_ahb", "ahb", "ispif_ahb", "csi0", "csi0_pix", "csi0_rdi", @@ -101,24 +101,184 @@ static const struct resources_ispif ispif_res = { }; -static const struct resources vfe_res = { +static const struct resources vfe_res_8x16[] = { /* VFE0 */ - .regulator = { NULL }, - .clock = { "top_ahb", "vfe0", "csi_vfe0", - "vfe_ahb", "vfe_axi", "ahb" }, - .clock_rate = { { 0 }, - { 50000000, 80000000, 100000000, 160000000, - 177780000, 200000000, 266670000, 320000000, - 400000000, 465000000 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 } }, - .reg = { "vfe0" }, - .interrupt = { "vfe0" } + { + .regulator = { NULL }, + .clock = { "top_ahb", "vfe0", "csi_vfe0", + "vfe_ahb", "vfe_axi", "ahb" }, + .clock_rate = { { 0 }, + { 50000000, 80000000, 100000000, 160000000, + 177780000, 200000000, 266670000, 320000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + } +}; + +static const struct resources csiphy_res_8x96[] = { + /* CSIPHY0 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy0", "csiphy0_clk_mux" }, + .interrupt = { "csiphy0" } + }, + + /* CSIPHY1 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy1", "csiphy1_clk_mux" }, + .interrupt = { "csiphy1" } + }, + + /* CSIPHY2 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy2", "csiphy2_clk_mux" }, + .interrupt = { "csiphy2" } + } +}; + +static const struct resources csid_res_8x96[] = { + /* CSID0 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + + /* CSID1 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, + + /* CSID2 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", + "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" } + }, + + /* CSID3 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", + "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid3" }, + .interrupt = { "csid3" } + } +}; + +static const struct resources_ispif ispif_res_8x96 = { + /* ISPIF */ + .clock = { "top_ahb", "ahb", "ispif_ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi", + "csi2", "csi2_pix", "csi2_rdi", + "csi3", "csi3_pix", "csi3_rdi" }, + .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = "ispif" +}; + +static const struct resources vfe_res_8x96[] = { + /* VFE0 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb", + "vfe0_ahb", "vfe_axi", "vfe0_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 75000000, 100000000, 300000000, + 320000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + }, + + /* VFE1 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb", + "vfe1_ahb", "vfe_axi", "vfe1_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 75000000, 100000000, 300000000, + 320000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" } + } }; /* @@ -345,11 +505,29 @@ static int camss_of_parse_ports(struct device *dev, */ static int camss_init_subdevices(struct camss *camss) { + const struct resources *csiphy_res; + const struct resources *csid_res; + const struct resources_ispif *ispif_res; + const struct resources *vfe_res; unsigned int i; int ret; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - ret = msm_csiphy_subdev_init(&camss->csiphy[i], + if (camss->version == CAMSS_8x16) { + csiphy_res = csiphy_res_8x16; + csid_res = csid_res_8x16; + ispif_res = &ispif_res_8x16; + vfe_res = vfe_res_8x16; + } else if (camss->version == CAMSS_8x96) { + csiphy_res = csiphy_res_8x96; + csid_res = csid_res_8x96; + ispif_res = &ispif_res_8x96; + vfe_res = vfe_res_8x96; + } else { + return -EINVAL; + } + + for (i = 0; i < camss->csiphy_num; i++) { + ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], &csiphy_res[i], i); if (ret < 0) { dev_err(camss->dev, @@ -359,8 +537,8 @@ static int camss_init_subdevices(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - ret = msm_csid_subdev_init(&camss->csid[i], + for (i = 0; i < camss->csid_num; i++) { + ret = msm_csid_subdev_init(camss, &camss->csid[i], &csid_res[i], i); if (ret < 0) { dev_err(camss->dev, @@ -370,17 +548,21 @@ static int camss_init_subdevices(struct camss *camss) } } - ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res); + ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); if (ret < 0) { dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", ret); return ret; } - ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res); - if (ret < 0) { - dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret); - return ret; + for (i = 0; i < camss->vfe_num; i++) { + ret = msm_vfe_subdev_init(camss, &camss->vfe[i], + &vfe_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Fail to init vfe%d sub-device: %d\n", i, ret); + return ret; + } } return 0; @@ -394,10 +576,10 @@ static int camss_init_subdevices(struct camss *camss) */ static int camss_register_entities(struct camss *camss) { - int i, j; + int i, j, k; int ret; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { + for (i = 0; i < camss->csiphy_num; i++) { ret = msm_csiphy_register_entity(&camss->csiphy[i], &camss->v4l2_dev); if (ret < 0) { @@ -408,7 +590,7 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { + for (i = 0; i < camss->csid_num; i++) { ret = msm_csid_register_entity(&camss->csid[i], &camss->v4l2_dev); if (ret < 0) { @@ -426,15 +608,19 @@ static int camss_register_entities(struct camss *camss) goto err_reg_ispif; } - ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register vfe entities: %d\n", - ret); - goto err_reg_vfe; + for (i = 0; i < camss->vfe_num; i++) { + ret = msm_vfe_register_entities(&camss->vfe[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register vfe%d entities: %d\n", + i, ret); + goto err_reg_vfe; + } } - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - for (j = 0; j < ARRAY_SIZE(camss->csid); j++) { + for (i = 0; i < camss->csiphy_num; i++) { + for (j = 0; j < camss->csid_num; j++) { ret = media_create_pad_link( &camss->csiphy[i].subdev.entity, MSM_CSIPHY_PAD_SRC, @@ -452,8 +638,8 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) { + for (i = 0; i < camss->csid_num; i++) { + for (j = 0; j < camss->ispif.line_num; j++) { ret = media_create_pad_link( &camss->csid[i].subdev.entity, MSM_CSID_PAD_SRC, @@ -471,39 +657,42 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) { - for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) { - ret = media_create_pad_link( - &camss->ispif.line[i].subdev.entity, - MSM_ISPIF_PAD_SRC, - &camss->vfe.line[j].subdev.entity, - MSM_VFE_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->ispif.line[i].subdev.entity.name, - camss->vfe.line[j].subdev.entity.name, - ret); - goto err_link; + for (i = 0; i < camss->ispif.line_num; i++) + for (k = 0; k < camss->vfe_num; k++) + for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { + ret = media_create_pad_link( + &camss->ispif.line[i].subdev.entity, + MSM_ISPIF_PAD_SRC, + &camss->vfe[k].line[j].subdev.entity, + MSM_VFE_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + camss->ispif.line[i].subdev.entity.name, + camss->vfe[k].line[j].subdev.entity.name, + ret); + goto err_link; + } } - } - } return 0; err_link: - msm_vfe_unregister_entities(&camss->vfe); + i = camss->vfe_num; err_reg_vfe: + for (i--; i >= 0; i--) + msm_vfe_unregister_entities(&camss->vfe[i]); + msm_ispif_unregister_entities(&camss->ispif); err_reg_ispif: - i = ARRAY_SIZE(camss->csid); + i = camss->csid_num; err_reg_csid: for (i--; i >= 0; i--) msm_csid_unregister_entity(&camss->csid[i]); - i = ARRAY_SIZE(camss->csiphy); + i = camss->csiphy_num; err_reg_csiphy: for (i--; i >= 0; i--) msm_csiphy_unregister_entity(&camss->csiphy[i]); @@ -521,14 +710,16 @@ static void camss_unregister_entities(struct camss *camss) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) + for (i = 0; i < camss->csiphy_num; i++) msm_csiphy_unregister_entity(&camss->csiphy[i]); - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) + for (i = 0; i < camss->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); msm_ispif_unregister_entities(&camss->ispif); - msm_vfe_unregister_entities(&camss->vfe); + + for (i = 0; i < camss->vfe_num; i++) + msm_vfe_unregister_entities(&camss->vfe[i]); } static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, @@ -620,6 +811,35 @@ static int camss_probe(struct platform_device *pdev) camss->dev = dev; platform_set_drvdata(pdev, camss); + if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { + camss->version = CAMSS_8x16; + camss->csiphy_num = 2; + camss->csid_num = 2; + camss->vfe_num = 1; + } else if (of_device_is_compatible(dev->of_node, + "qcom,msm8996-camss")) { + camss->version = CAMSS_8x96; + camss->csiphy_num = 3; + camss->csid_num = 4; + camss->vfe_num = 2; + } else { + return -EINVAL; + } + + camss->csiphy = kcalloc(camss->csiphy_num, sizeof(*camss->csiphy), + GFP_KERNEL); + if (!camss->csiphy) + return -ENOMEM; + + camss->csid = kcalloc(camss->csid_num, sizeof(*camss->csid), + GFP_KERNEL); + if (!camss->csid) + return -ENOMEM; + + camss->vfe = kcalloc(camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); + if (!camss->vfe) + return -ENOMEM; + ret = camss_of_parse_ports(dev, &camss->notifier); if (ret < 0) return ret; @@ -703,9 +923,12 @@ void camss_delete(struct camss *camss) */ static int camss_remove(struct platform_device *pdev) { + unsigned int i; + struct camss *camss = platform_get_drvdata(pdev); - msm_vfe_stop_streaming(&camss->vfe); + for (i = 0; i < camss->vfe_num; i++) + msm_vfe_stop_streaming(&camss->vfe[i]); v4l2_async_notifier_unregister(&camss->notifier); camss_unregister_entities(camss); @@ -718,6 +941,7 @@ static int camss_remove(struct platform_device *pdev) static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss" }, + { .compatible = "qcom,msm8996-camss" }, { } }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index fb1c2f946e71..dff1045a8a77 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -23,9 +23,6 @@ #include "camss-ispif.h" #include "camss-vfe.h" -#define CAMSS_CSID_NUM 2 -#define CAMSS_CSIPHY_NUM 2 - #define to_camss(ptr_module) \ container_of(ptr_module, struct camss, ptr_module) @@ -42,7 +39,7 @@ #define to_device_index(ptr_module, index) \ (to_camss_index(ptr_module, index)->dev) -#define CAMSS_RES_MAX 15 +#define CAMSS_RES_MAX 17 struct resources { char *regulator[CAMSS_RES_MAX]; @@ -59,15 +56,24 @@ struct resources_ispif { char *interrupt; }; +enum camss_version { + CAMSS_8x16, + CAMSS_8x96, +}; + struct camss { + enum camss_version version; struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; struct media_device media_dev; struct device *dev; - struct csiphy_device csiphy[CAMSS_CSIPHY_NUM]; - struct csid_device csid[CAMSS_CSID_NUM]; + int csiphy_num; + struct csiphy_device *csiphy; + int csid_num; + struct csid_device *csid; struct ispif_device ispif; - struct vfe_device vfe; + int vfe_num; + struct vfe_device *vfe; atomic_t ref_count; }; -- cgit v1.2.3-70-g09d2 From 02afa816dbbf0bc648d22a8472f17236df886af5 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:27 -0400 Subject: media: camss: Add basic runtime PM support There is a PM domain for each of the VFE hardware modules. Add support for basic runtime PM support to be able to control the PM domains. When a PM domain needs to be powered on - a device link is created. When a PM domain needs to be powered off - its device link is removed. This allows separate and independent control of the PM domains. Suspend/Resume is still not supported. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 13 ++++- drivers/media/platform/qcom/camss/camss-csiphy.c | 15 +++++- drivers/media/platform/qcom/camss/camss-ispif.c | 26 ++++++++-- drivers/media/platform/qcom/camss/camss-vfe.c | 17 +++++++ drivers/media/platform/qcom/camss/camss.c | 63 ++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 11 +++++ 6 files changed, 139 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 627ef44cf895..3ba087fd95a1 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -316,19 +317,27 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) if (on) { u32 hw_version; - ret = regulator_enable(csid->vdda); + ret = pm_runtime_get_sync(dev); if (ret < 0) return ret; + ret = regulator_enable(csid->vdda); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + ret = csid_set_clock_rates(csid); if (ret < 0) { regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); if (ret < 0) { regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } @@ -339,6 +348,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } @@ -348,6 +358,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); ret = regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); } return ret; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 0383e94996a3..4aeaedb69786 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -240,13 +241,21 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) u8 hw_version; int ret; - ret = csiphy_set_clock_rates(csiphy); + ret = pm_runtime_get_sync(dev); if (ret < 0) return ret; + ret = csiphy_set_clock_rates(csiphy); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } enable_irq(csiphy->irq); @@ -259,6 +268,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) disable_irq(csiphy->irq); camss_disable_clocks(csiphy->nclocks, csiphy->clock); + + pm_runtime_put_sync(dev); } return 0; diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index ed50cc55e3d5..2c6c0d2d6acf 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,14 @@ static int ispif_reset(struct ispif_device *ispif) u32 val; int ret; + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0); + if (ret < 0) + return ret; + + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1); + if (ret < 0) + return ret; + ret = camss_enable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset, to_device(ispif)); @@ -201,12 +210,15 @@ static int ispif_reset(struct ispif_device *ispif) msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); if (!time) { dev_err(to_device(ispif), "ISPIF reset timeout\n"); - return -EIO; + ret = -EIO; } camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); - return 0; + camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0); + camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1); + + return ret; } /* @@ -232,12 +244,19 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } - ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto exit; + ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); + if (ret < 0) { + pm_runtime_put_sync(dev); + goto exit; + } + ret = ispif_reset(ispif); if (ret < 0) { + pm_runtime_put_sync(dev); camss_disable_clocks(ispif->nclocks, ispif->clock); goto exit; } @@ -252,6 +271,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } else if (ispif->power_count == 1) { camss_disable_clocks(ispif->nclocks, ispif->clock); + pm_runtime_put_sync(dev); } ispif->power_count--; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 3f589c448cd2..474e1dd02891 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1981,6 +1982,14 @@ static int vfe_get(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { + ret = camss_pm_domain_on(vfe->camss, vfe->id); + if (ret < 0) + goto error_pm_domain; + + ret = pm_runtime_get_sync(vfe->camss->dev); + if (ret < 0) + goto error_pm_runtime_get; + ret = vfe_set_clock_rates(vfe); if (ret < 0) goto error_clocks; @@ -2012,6 +2021,12 @@ error_reset: camss_disable_clocks(vfe->nclocks, vfe->clock); error_clocks: + pm_runtime_put_sync(vfe->camss->dev); + +error_pm_runtime_get: + camss_pm_domain_off(vfe->camss, vfe->id); + +error_pm_domain: mutex_unlock(&vfe->power_lock); return ret; @@ -2034,6 +2049,8 @@ static void vfe_put(struct vfe_device *vfe) vfe_halt(vfe); } camss_disable_clocks(vfe->nclocks, vfe->clock); + pm_runtime_put_sync(vfe->camss->dev); + camss_pm_domain_off(vfe->camss, vfe->id); } vfe->power_count--; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 171e2c9d669e..dcc0c30ef1b1 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -393,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) return 0; } +int camss_pm_domain_on(struct camss *camss, int id) +{ + if (camss->version == CAMSS_8x96) { + camss->genpd_link[id] = device_link_add(camss->dev, + camss->genpd[id], DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); + + if (!camss->genpd_link[id]) + return -EINVAL; + } + + return 0; +} + +void camss_pm_domain_off(struct camss *camss, int id) +{ + if (camss->version == CAMSS_8x96) + device_link_del(camss->genpd_link[id]); +} + /* * camss_of_parse_endpoint_node - Parse port endpoint node * @dev: Device @@ -896,6 +918,23 @@ static int camss_probe(struct platform_device *pdev) } } + if (camss->version == CAMSS_8x96) { + camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( + camss->dev, PM_DOMAIN_VFE0); + if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) + return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]); + + camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id( + camss->dev, PM_DOMAIN_VFE1); + if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) { + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], + true); + return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]); + } + } + + pm_runtime_enable(dev); + return 0; err_register_subdevs: @@ -912,6 +951,13 @@ void camss_delete(struct camss *camss) media_device_unregister(&camss->media_dev); media_device_cleanup(&camss->media_dev); + pm_runtime_disable(camss->dev); + + if (camss->version == CAMSS_8x96) { + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); + } + kfree(camss); } @@ -947,12 +993,29 @@ static const struct of_device_id camss_dt_match[] = { MODULE_DEVICE_TABLE(of, camss_dt_match); +static int camss_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int camss_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops camss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) +}; + static struct platform_driver qcom_camss_driver = { .probe = camss_probe, .remove = camss_remove, .driver = { .name = "qcom-camss", .of_match_table = camss_dt_match, + .pm = &camss_pm_ops, }, }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index dff1045a8a77..418996d8dad8 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -10,6 +10,7 @@ #ifndef QC_MSM_CAMSS_H #define QC_MSM_CAMSS_H +#include #include #include #include @@ -56,6 +57,12 @@ struct resources_ispif { char *interrupt; }; +enum pm_domain { + PM_DOMAIN_VFE0, + PM_DOMAIN_VFE1, + PM_DOMAIN_COUNT +}; + enum camss_version { CAMSS_8x16, CAMSS_8x96, @@ -75,6 +82,8 @@ struct camss { int vfe_num; struct vfe_device *vfe; atomic_t ref_count; + struct device *genpd[PM_DOMAIN_COUNT]; + struct device_link *genpd_link[PM_DOMAIN_COUNT]; }; struct camss_camera_interface { @@ -99,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); void camss_disable_clocks(int nclocks, struct camss_clock *clock); int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock); +int camss_pm_domain_on(struct camss *camss, int id); +void camss_pm_domain_off(struct camss *camss, int id); void camss_delete(struct camss *camss); #endif /* QC_MSM_CAMSS_H */ -- cgit v1.2.3-70-g09d2 From 516e8f0f8912b3779e4259c824953a23c4108fe8 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:28 -0400 Subject: media: camss: csiphy: Split to hardware dependent and independent parts This will allow to add support for different hardware. Signed-off-by: Todor Tomov [hans.verkuil@cisco.com: remove trailing empty line] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csiphy-2ph-1-0.c | 172 +++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-csiphy.c | 171 +++----------------- drivers/media/platform/qcom/camss/camss-csiphy.h | 17 ++ 4 files changed, 213 insertions(+), 148 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 3c4024fbb768..0446b242f7f8 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -3,6 +3,7 @@ qcom-camss-objs += \ camss.o \ camss-csid.o \ + camss-csiphy-2ph-1-0.o \ camss-csiphy.o \ camss-ispif.o \ camss-vfe.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c new file mode 100644 index 000000000000..5485d1fd31d7 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csiphy-2ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 2phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#include "camss-csiphy.h" + +#include +#include + +#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) +#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) +#define CAMSS_CSI_PHY_GLBL_RESET 0x140 +#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 +#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 +#define CAMSS_CSI_PHY_HW_VERSION 0x188 +#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) +#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec +#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 + +static void csiphy_hw_version_read(struct csiphy_device *csiphy, + struct device *dev) +{ + u8 hw_version = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_HW_VERSION); + + dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); +} + +/* + * csiphy_reset - Perform software reset on CSIPHY module + * @csiphy: CSIPHY device + */ +static void csiphy_reset(struct csiphy_device *csiphy) +{ + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + usleep_range(5000, 8000); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); +} + +/* + * csiphy_settle_cnt_calc - Calculate settle count value + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter pixel clock + * frequency. + * + * Return settle count value or 0 if the CSI2 pixel clock + * frequency is not available + */ +static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, + u32 timer_clk_rate) +{ + u32 mipi_clock; /* Hz */ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_prepare_zero_min; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + mipi_clock = pixel_clock * bpp / (2 * num_lanes); + ui = div_u64(1000000000000LL, mipi_clock); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_prepare_zero_min = 145000 + 10 * ui; + t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 1; + + return settle_cnt; +} + +static void csiphy_lanes_enable(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 settle_cnt; + u8 val; + int i = 0; + + settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data, + csiphy->timer_clk_rate); + + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_T_WAKEUP_CFG0); + + val = 0x1; + val |= lane_mask << 1; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); + + val = cfg->combo_mode << 4; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + + while (lane_mask) { + if (lane_mask & 0x1) { + writel_relaxed(0x10, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(i)); + writel_relaxed(settle_cnt, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG3(i)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + } + + lane_mask >>= 1; + i++; + } +} + +static void csiphy_lanes_disable(struct csiphy_device *csiphy, u8 lane_mask) +{ + int i = 0; + + while (lane_mask) { + if (lane_mask & 0x1) + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(i)); + + lane_mask >>= 1; + i++; + } + + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); +} + +/* + * csiphy_isr - CSIPHY module interrupt handler + * @irq: Interrupt line + * @dev: CSIPHY device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csiphy_isr(int irq, void *dev) +{ + struct csiphy_device *csiphy = dev; + u8 i; + + for (i = 0; i < 8; i++) { + u8 val = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); + writel_relaxed(val, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + } + + return IRQ_HANDLED; +} + +const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = { + .hw_version_read = csiphy_hw_version_read, + .reset = csiphy_reset, + .lanes_enable = csiphy_lanes_enable, + .lanes_disable = csiphy_lanes_disable, + .isr = csiphy_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 4aeaedb69786..8d10e85eac62 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -23,18 +23,6 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) -#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) -#define CAMSS_CSI_PHY_GLBL_RESET 0x140 -#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 -#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 -#define CAMSS_CSI_PHY_HW_VERSION 0x188 -#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) -#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec -#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 - static const struct { u32 code; u8 bpp; @@ -124,32 +112,6 @@ static u8 csiphy_get_bpp(u32 code) return csiphy_formats[0].bpp; } -/* - * csiphy_isr - CSIPHY module interrupt handler - * @irq: Interrupt line - * @dev: CSIPHY device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t csiphy_isr(int irq, void *dev) -{ - struct csiphy_device *csiphy = dev; - u8 i; - - for (i = 0; i < 8; i++) { - u8 val = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); - writel_relaxed(val, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - return IRQ_HANDLED; -} - /* * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module * @csiphy: CSIPHY device @@ -214,17 +176,6 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) return 0; } -/* - * csiphy_reset - Perform software reset on CSIPHY module - * @csiphy: CSIPHY device - */ -static void csiphy_reset(struct csiphy_device *csiphy) -{ - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - usleep_range(5000, 8000); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); -} - /* * csiphy_set_power - Power on/off CSIPHY module * @sd: CSIPHY V4L2 subdevice @@ -238,7 +189,6 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) struct device *dev = csiphy->camss->dev; if (on) { - u8 hw_version; int ret; ret = pm_runtime_get_sync(dev); @@ -259,11 +209,9 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) enable_irq(csiphy->irq); - csiphy_reset(csiphy); + csiphy->ops->reset(csiphy); - hw_version = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_HW_VERSION); - dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); + csiphy->ops->hw_version_read(csiphy, dev); } else { disable_irq(csiphy->irq); @@ -295,77 +243,34 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) } /* - * csiphy_settle_cnt_calc - Calculate settle count value + * csiphy_stream_on - Enable streaming on CSIPHY module * @csiphy: CSIPHY device * - * Helper function to calculate settle count value. This is - * based on the CSI2 T_hs_settle parameter which in turn - * is calculated based on the CSI2 transmitter pixel clock - * frequency. + * Helper function to enable streaming on CSIPHY module. + * Main configuration of CSIPHY module is also done here. * - * Return settle count value or 0 if the CSI2 pixel clock - * frequency is not available + * Return 0 on success or a negative error code otherwise */ -static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) +static int csiphy_stream_on(struct csiphy_device *csiphy) { - u8 bpp = csiphy_get_bpp( - csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); - u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; - u32 pixel_clock; /* Hz */ - u32 mipi_clock; /* Hz */ - u32 ui; /* ps */ - u32 timer_period; /* ps */ - u32 t_hs_prepare_max; /* ps */ - u32 t_hs_prepare_zero_min; /* ps */ - u32 t_hs_settle; /* ps */ - u8 settle_cnt; + struct csiphy_config *cfg = &csiphy->cfg; + u32 pixel_clock; + u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); + u8 bpp = csiphy_get_bpp(csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 val; int ret; ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); if (ret) { dev_err(csiphy->camss->dev, "Cannot get CSI2 transmitter's pixel clock\n"); - return 0; + return -EINVAL; } if (!pixel_clock) { dev_err(csiphy->camss->dev, "Got pixel clock == 0, cannot continue\n"); - return 0; - } - - mipi_clock = pixel_clock * bpp / (2 * num_lanes); - ui = div_u64(1000000000000LL, mipi_clock); - ui /= 2; - t_hs_prepare_max = 85000 + 6 * ui; - t_hs_prepare_zero_min = 145000 + 10 * ui; - t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; - - timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate); - settle_cnt = t_hs_settle / timer_period - 1; - - return settle_cnt; -} - -/* - * csiphy_stream_on - Enable streaming on CSIPHY module - * @csiphy: CSIPHY device - * - * Helper function to enable streaming on CSIPHY module. - * Main configuration of CSIPHY module is also done here. - * - * Return 0 on success or a negative error code otherwise - */ -static int csiphy_stream_on(struct csiphy_device *csiphy) -{ - struct csiphy_config *cfg = &csiphy->cfg; - u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); - u8 settle_cnt; - u8 val; - int i = 0; - - settle_cnt = csiphy_settle_cnt_calc(csiphy); - if (!settle_cnt) return -EINVAL; + } val = readl_relaxed(csiphy->base_clk_mux); if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { @@ -378,33 +283,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) writel_relaxed(val, csiphy->base_clk_mux); wmb(); - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_T_WAKEUP_CFG0); - - val = 0x1; - val |= lane_mask << 1; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); - - val = cfg->combo_mode << 4; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - - while (lane_mask) { - if (lane_mask & 0x1) { - writel_relaxed(0x10, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - writel_relaxed(settle_cnt, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG3(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - lane_mask >>= 1; - i++; - } + csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask); return 0; } @@ -418,18 +297,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) static void csiphy_stream_off(struct csiphy_device *csiphy) { u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg); - int i = 0; - while (lane_mask) { - if (lane_mask & 0x1) - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - - lane_mask >>= 1; - i++; - } - - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); + csiphy->ops->lanes_disable(csiphy, lane_mask); } @@ -696,6 +565,11 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->id = id; csiphy->cfg.combo_mode = 0; + if (camss->version == CAMSS_8x16) + csiphy->ops = &csiphy_ops_2ph_1_0; + else + return -EINVAL; + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -724,7 +598,8 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->irq = r->start; snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy_isr, + + ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 728dfef18676..edad941d3bbe 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -11,6 +11,7 @@ #define QC_MSM_CAMSS_CSIPHY_H #include +#include #include #include #include @@ -41,6 +42,19 @@ struct csiphy_config { struct csiphy_csi2_cfg *csi2; }; +struct csiphy_device; + +struct csiphy_hw_ops { + void (*hw_version_read)(struct csiphy_device *csiphy, + struct device *dev); + void (*reset)(struct csiphy_device *csiphy); + void (*lanes_enable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask); + void (*lanes_disable)(struct csiphy_device *csiphy, u8 lane_mask); + irqreturn_t (*isr)(int irq, void *dev); +}; + struct csiphy_device { struct camss *camss; u8 id; @@ -55,6 +69,7 @@ struct csiphy_device { u32 timer_clk_rate; struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; + const struct csiphy_hw_ops *ops; }; struct resources; @@ -68,4 +83,6 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); +extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; + #endif /* QC_MSM_CAMSS_CSIPHY_H */ -- cgit v1.2.3-70-g09d2 From 369f81f350b5c03599d8f82e31f9a916d7bfe80a Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:29 -0400 Subject: media: camss: csiphy: Unify lane handling Restructure lane configuration so it is simpler and will allow similar (although not the same) handling for different hardware versions. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../platform/qcom/camss/camss-csiphy-2ph-1-0.c | 48 ++++++++++++---------- drivers/media/platform/qcom/camss/camss-csiphy.c | 4 +- drivers/media/platform/qcom/camss/camss-csiphy.h | 3 +- 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c index 5485d1fd31d7..c832539397d7 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c @@ -86,7 +86,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, { struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; u8 settle_cnt; - u8 val; + u8 val, l = 0; int i = 0; settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data, @@ -104,34 +104,38 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, val = cfg->combo_mode << 4; writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - while (lane_mask) { - if (lane_mask & 0x1) { - writel_relaxed(0x10, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - writel_relaxed(settle_cnt, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG3(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - lane_mask >>= 1; - i++; + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = c->clk.pos; + else + l = c->data[i].pos; + + writel_relaxed(0x10, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(l)); + writel_relaxed(settle_cnt, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG3(l)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_MASKn(l)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(l)); } } -static void csiphy_lanes_disable(struct csiphy_device *csiphy, u8 lane_mask) +static void csiphy_lanes_disable(struct csiphy_device *csiphy, + struct csiphy_config *cfg) { + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 l = 0; int i = 0; - while (lane_mask) { - if (lane_mask & 0x1) - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = c->clk.pos; + else + l = c->data[i].pos; - lane_mask >>= 1; - i++; + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(l)); } writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 8d10e85eac62..d35eea03ebf8 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -296,9 +296,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg); - - csiphy->ops->lanes_disable(csiphy, lane_mask); + csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index edad941d3bbe..e3dd25702b01 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -51,7 +51,8 @@ struct csiphy_hw_ops { void (*lanes_enable)(struct csiphy_device *csiphy, struct csiphy_config *cfg, u32 pixel_clock, u8 bpp, u8 lane_mask); - void (*lanes_disable)(struct csiphy_device *csiphy, u8 lane_mask); + void (*lanes_disable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg); irqreturn_t (*isr)(int irq, void *dev); }; -- cgit v1.2.3-70-g09d2 From 4138a88b6093e1f8bf1b8a95c653bec6c854f119 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:30 -0400 Subject: media: camss: csiphy: Add support for 8x96 Add CSIPHY hardware dependent part for 8x96. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csiphy-3ph-1-0.c | 256 +++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-csiphy.c | 2 + drivers/media/platform/qcom/camss/camss-csiphy.h | 1 + 4 files changed, 260 insertions(+) create mode 100644 drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 0446b242f7f8..36b9f7cc3340 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -4,6 +4,7 @@ qcom-camss-objs += \ camss.o \ camss-csid.o \ camss-csiphy-2ph-1-0.o \ + camss-csiphy-3ph-1-0.o \ camss-csiphy.o \ camss-ispif.o \ camss-vfe.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c new file mode 100644 index 000000000000..bcd0dfd33618 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csiphy-3ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#include "camss-csiphy.h" + +#include +#include + +#define CSIPHY_3PH_LNn_CFG1(n) (0x000 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG (BIT(7) | BIT(6)) +#define CSIPHY_3PH_LNn_CFG2(n) (0x004 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT BIT(3) +#define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4 +#define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02 +#define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50 +#define CSIPHY_3PH_LNn_TEST_IMP(n) (0x01c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP 0xa +#define CSIPHY_3PH_LNn_MISC1(n) (0x028 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_MISC1_IS_CLKLANE BIT(2) +#define CSIPHY_3PH_LNn_CFG6(n) (0x02c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT BIT(0) +#define CSIPHY_3PH_LNn_CFG7(n) (0x030 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG7_SWI_T_INIT 0x2 +#define CSIPHY_3PH_LNn_CFG8(n) (0x034 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP BIT(0) +#define CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE BIT(1) +#define CSIPHY_3PH_LNn_CFG9(n) (0x038 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP 0x1 +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8 + +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n)) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) +#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n)) + +static void csiphy_hw_version_read(struct csiphy_device *csiphy, + struct device *dev) +{ + u32 hw_version; + + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, + csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); + + hw_version = readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(12)); + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(13)) << 8; + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(14)) << 16; + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24; + + dev_err(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); +} + +/* + * csiphy_reset - Perform software reset on CSIPHY module + * @csiphy: CSIPHY device + */ +static void csiphy_reset(struct csiphy_device *csiphy) +{ + writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); + usleep_range(5000, 8000); + writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); +} + +static irqreturn_t csiphy_isr(int irq, void *dev) +{ + struct csiphy_device *csiphy = dev; + int i; + + for (i = 0; i < 11; i++) { + int c = i + 22; + u8 val = readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(i)); + + writel_relaxed(val, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(c)); + } + + writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10)); + writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10)); + + for (i = 22; i < 33; i++) + writel_relaxed(0x0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i)); + + return IRQ_HANDLED; +} + +/* + * csiphy_settle_cnt_calc - Calculate settle count value + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter pixel clock + * frequency. + * + * Return settle count value or 0 if the CSI2 pixel clock + * frequency is not available + */ +static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, + u32 timer_clk_rate) +{ + u32 mipi_clock; /* Hz */ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + mipi_clock = pixel_clock * bpp / (2 * num_lanes); + ui = div_u64(1000000000000LL, mipi_clock); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_settle = t_hs_prepare_max; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 6; + + return settle_cnt; +} + +static void csiphy_lanes_enable(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 settle_cnt; + u8 val, l = 0; + int i; + + settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data, + csiphy->timer_clk_rate); + + val = BIT(c->clk.pos); + for (i = 0; i < c->num_data; i++) + val |= BIT(c->data[i].pos * 2); + + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5)); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); + + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = 7; + else + l = c->data[i].pos * 2; + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + val |= 0x17; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG2(l)); + + val = settle_cnt; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG3(l)); + + val = CSIPHY_3PH_LNn_CFG5_T_HS_DTERM | + CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG5(l)); + + val = CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG6(l)); + + val = CSIPHY_3PH_LNn_CFG7_SWI_T_INIT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG7(l)); + + val = CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP | + CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG8(l)); + + val = CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG9(l)); + + val = CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_TEST_IMP(l)); + + val = CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL; + writel_relaxed(val, csiphy->base + + CSIPHY_3PH_LNn_CSI_LANE_CTRL15(l)); + } + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG4(l)); + + val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_MISC1(l)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(12)); + + val = 0xfb; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(13)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(14)); + + val = 0x7f; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(15)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(16)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(17)); + + val = 0xef; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(18)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(19)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(20)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(21)); +} + +static void csiphy_lanes_disable(struct csiphy_device *csiphy, + struct csiphy_config *cfg) +{ + writel_relaxed(0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5)); + + writel_relaxed(0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); +} + +const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = { + .hw_version_read = csiphy_hw_version_read, + .reset = csiphy_reset, + .lanes_enable = csiphy_lanes_enable, + .lanes_disable = csiphy_lanes_disable, + .isr = csiphy_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index d35eea03ebf8..7da705147966 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -565,6 +565,8 @@ int msm_csiphy_subdev_init(struct camss *camss, if (camss->version == CAMSS_8x16) csiphy->ops = &csiphy_ops_2ph_1_0; + else if (camss->version == CAMSS_8x96) + csiphy->ops = &csiphy_ops_3ph_1_0; else return -EINVAL; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index e3dd25702b01..5debe46b6cf1 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -85,5 +85,6 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; +extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0; #endif /* QC_MSM_CAMSS_CSIPHY_H */ -- cgit v1.2.3-70-g09d2 From 2a05493b5d549ee9a1ecea8772bcc7d9ec7f3d69 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:31 -0400 Subject: media: camss: csid: Add support for 8x96 CSID hardware modules on 8x16 and 8x96 are similar. There is no need to duplicate the code by adding separate versions. Just update the register macros to return the correct register addresses. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 60 ++++++++++++++++---------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 3ba087fd95a1..915835ee3271 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -27,21 +27,26 @@ #define CAMSS_CSID_HW_VERSION 0x0 #define CAMSS_CSID_CORE_CTRL_0 0x004 #define CAMSS_CSID_CORE_CTRL_1 0x008 -#define CAMSS_CSID_RST_CMD 0x00c -#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n)) -#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n)) -#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060 -#define CAMSS_CSID_IRQ_MASK 0x064 -#define CAMSS_CSID_IRQ_STATUS 0x068 -#define CAMSS_CSID_TG_CTRL 0x0a0 +#define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010) +#define CAMSS_CSID_CID_LUT_VC_n(v, n) \ + (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG(v, n) \ + (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n)) +#define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064) +#define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068) +#define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c) +#define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8) #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 -#define CAMSS_CSID_TG_VC_CFG 0x0a4 +#define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac) #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f -#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \ + (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \ + (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \ + (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n)) #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 #define DATA_TYPE_YUV422_8BIT 0x1e @@ -203,10 +208,11 @@ static const struct csid_fmts *csid_get_fmt_entry(u32 code) static irqreturn_t csid_isr(int irq, void *dev) { struct csid_device *csid = dev; + enum camss_version ver = csid->camss->version; u32 value; - value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); - writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); + value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver)); + writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver)); if ((value >> 11) & 0x1) complete(&csid->reset_complete); @@ -289,7 +295,8 @@ static int csid_reset(struct csid_device *csid) reinit_completion(&csid->reset_complete); - writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); + writel_relaxed(0x7fff, csid->base + + CAMSS_CSID_RST_CMD(csid->camss->version)); time = wait_for_completion_timeout(&csid->reset_complete, msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); @@ -377,6 +384,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) { struct csid_device *csid = v4l2_get_subdevdata(sd); struct csid_testgen_config *tg = &csid->testgen; + enum camss_version ver = csid->camss->version; u32 val; if (enable) { @@ -409,13 +417,14 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) /* 1:0 VC */ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); - writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_VC_CFG(ver)); /* 28:16 bytes per lines, 12:0 num of lines */ val = ((num_bytes_per_line & 0x1fff) << 16) | (num_lines & 0x1fff); writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_0(0)); + CAMSS_CSID_TG_DT_n_CGG_0(ver, 0)); dt = csid_get_fmt_entry( csid->fmt[MSM_CSID_PAD_SRC].code)->data_type; @@ -423,12 +432,12 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) /* 5:0 data type */ val = dt; writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_1(0)); + CAMSS_CSID_TG_DT_n_CGG_1(ver, 0)); /* 2:0 output test pattern */ val = tg->payload_mode; writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_2(0)); + CAMSS_CSID_TG_DT_n_CGG_2(ver, 0)); df = csid_get_fmt_entry( csid->fmt[MSM_CSID_PAD_SRC].code)->decode_format; @@ -457,22 +466,27 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) dt_shift = (cid % 4) * 8; - val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + val = readl_relaxed(csid->base + + CAMSS_CSID_CID_LUT_VC_n(ver, vc)); val &= ~(0xff << dt_shift); val |= dt << dt_shift; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + writel_relaxed(val, csid->base + + CAMSS_CSID_CID_LUT_VC_n(ver, vc)); val = (df << 4) | 0x3; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); + writel_relaxed(val, csid->base + + CAMSS_CSID_CID_n_CFG(ver, cid)); if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_ENABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_CTRL(ver)); } } else { if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_DISABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_CTRL(ver)); } } -- cgit v1.2.3-70-g09d2 From e08c7f8696200a22b7f5a05f139231ad202efdc7 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:32 -0400 Subject: media: camss: ispif: Add support for 8x96 ISPIF hardware modules on 8x16 and 8x96 are similar. However on 8x96 the ISPIF routes data to two VFE hardware modules. Add separate interrupt handler for 8x96 to handle the additional interrupts. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-ispif.c | 76 ++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 2c6c0d2d6acf..ae8073274624 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -116,13 +116,77 @@ static const u32 ispif_formats[] = { }; /* - * ispif_isr - ISPIF module interrupt handler + * ispif_isr_8x96 - ISPIF module interrupt handler for 8x96 * @irq: Interrupt line * @dev: ISPIF device * * Return IRQ_HANDLED on success */ -static irqreturn_t ispif_isr(int irq, void *dev) +static irqreturn_t ispif_isr_8x96(int irq, void *dev) +{ + struct ispif_device *ispif = dev; + u32 value0, value1, value2, value3, value4, value5; + + value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); + value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0)); + value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0)); + value3 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(1)); + value4 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(1)); + value5 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(1)); + + writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0)); + writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0)); + writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0)); + writel_relaxed(value3, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(1)); + writel_relaxed(value4, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(1)); + writel_relaxed(value5, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(1)); + + writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); + + if ((value0 >> 27) & 0x1) + complete(&ispif->reset_complete); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n"); + + if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n"); + + if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 pix0 overflow\n"); + + if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi0 overflow\n"); + + if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 pix1 overflow\n"); + + if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi1 overflow\n"); + + if (unlikely(value5 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi2 overflow\n"); + + return IRQ_HANDLED; +} + +/* + * ispif_isr_8x16 - ISPIF module interrupt handler for 8x16 + * @irq: Interrupt line + * @dev: ISPIF device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t ispif_isr_8x16(int irq, void *dev) { struct ispif_device *ispif = dev; u32 value0, value1, value2; @@ -959,8 +1023,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, ispif->irq = r->start; snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", dev_name(dev), MSM_ISPIF_NAME); - ret = devm_request_irq(dev, ispif->irq, ispif_isr, + if (to_camss(ispif)->version == CAMSS_8x16) + ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); + else if (to_camss(ispif)->version == CAMSS_8x96) + ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96, + IRQF_TRIGGER_RISING, ispif->irq_name, ispif); + else + ret = -EINVAL; if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); return ret; -- cgit v1.2.3-70-g09d2 From 051a01ac9cf141f0d555488fd0c060949bd4e8b6 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:33 -0400 Subject: media: camss: vfe: Split to hardware dependent and independent parts This will allow to add support for different hardware. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-vfe-4-1.c | 1006 +++++++++++++++++++ drivers/media/platform/qcom/camss/camss-vfe.c | 1074 ++------------------- drivers/media/platform/qcom/camss/camss-vfe.h | 73 +- 4 files changed, 1169 insertions(+), 985 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-4-1.c (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 36b9f7cc3340..38dc56e1630c 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -7,6 +7,7 @@ qcom-camss-objs += \ camss-csiphy-3ph-1-0.o \ camss-csiphy.o \ camss-ispif.o \ + camss-vfe-4-1.o \ camss-vfe.o \ camss-video.o \ diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c new file mode 100644 index 000000000000..070c0c381f1b --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-4-1.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.1 + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + */ + +#include +#include + +#include "camss-vfe.h" + +#define VFE_0_HW_VERSION 0x000 + +#define VFE_0_GLOBAL_RESET_CMD 0x00c +#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0) +#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1) +#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2) +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3) +#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4) +#define VFE_0_GLOBAL_RESET_CMD_TIMER BIT(5) +#define VFE_0_GLOBAL_RESET_CMD_PM BIT(6) +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(7) +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(8) + +#define VFE_0_MODULE_CFG 0x018 +#define VFE_0_MODULE_CFG_DEMUX BIT(2) +#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE BIT(3) +#define VFE_0_MODULE_CFG_SCALE_ENC BIT(23) +#define VFE_0_MODULE_CFG_CROP_ENC BIT(27) + +#define VFE_0_CORE_CFG 0x01c +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 + +#define VFE_0_IRQ_CMD 0x024 +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_0_IRQ_MASK_0 0x028 +#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1) +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_MASK_1 0x02c +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0) +#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7) +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9) +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_CLEAR_0 0x030 +#define VFE_0_IRQ_CLEAR_1 0x034 + +#define VFE_0_IRQ_STATUS_0 0x038 +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_STATUS_1 0x03c +#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7) +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 +#define VFE_0_VIOLATION_STATUS 0x48 + +#define VFE_0_BUS_CMD 0x4c +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x) + +#define VFE_0_BUS_CFG 0x050 + +#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(1) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2) + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ + (0x088 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ + (0x08c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff + +#define VFE_0_BUS_PING_PONG_STATUS 0x268 + +#define VFE_0_BUS_BDG_CMD 0x2c0 +#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 + +#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 +#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 +#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 +#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc +#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 +#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 +#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc +#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 +#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 + +#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) +#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2) +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 +#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) BIT(16 + (r)) + +#define VFE_0_CAMIF_CMD 0x2f4 +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_NO_CHANGE 3 +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2) +#define VFE_0_CAMIF_CFG 0x2f8 +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6) +#define VFE_0_CAMIF_FRAME_CFG 0x300 +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 +#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 +#define VFE_0_CAMIF_STATUS 0x31c +#define VFE_0_CAMIF_STATUS_HALT BIT(31) + +#define VFE_0_REG_UPDATE 0x378 +#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n)) +#define VFE_0_REG_UPDATE_line_n(n) \ + ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) + +#define VFE_0_DEMUX_CFG 0x424 +#define VFE_0_DEMUX_CFG_PERIOD 0x3 +#define VFE_0_DEMUX_GAIN_0 0x428 +#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) +#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) +#define VFE_0_DEMUX_GAIN_1 0x42c +#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) +#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) +#define VFE_0_DEMUX_EVEN_CFG 0x438 +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 +#define VFE_0_DEMUX_ODD_CFG 0x43c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 + +#define VFE_0_SCALE_ENC_Y_CFG 0x75c +#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 +#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 +#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c +#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 +#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c +#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 +#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 + +#define VFE_0_CROP_ENC_Y_WIDTH 0x854 +#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 +#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c +#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 + +#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 +#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) +#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 +#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) + +#define VFE_0_CGC_OVERRIDE_1 0x974 +#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) BIT(x) + +#define CAMIF_TIMEOUT_SLEEP_US 1000 +#define CAMIF_TIMEOUT_ALL_US 1000000 + +#define MSM_VFE_VFE0_UB_SIZE 1023 +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) + +static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); + + dev_dbg(dev, "VFE HW Version = 0x%08x\n", hw_version); +} + +static u16 vfe_get_ub_size(u8 vfe_id) +{ + if (vfe_id == 0) + return MSM_VFE_VFE0_UB_SIZE_RDI; + + return 0; +} + +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits & ~clr_bits, vfe->base + reg); +} + +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits | set_bits, vfe->base + reg); +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | + VFE_0_GLOBAL_RESET_CMD_BUS_MISR | + VFE_0_GLOBAL_RESET_CMD_PM | + VFE_0_GLOBAL_RESET_CMD_TIMER | + VFE_0_GLOBAL_RESET_CMD_REGISTER | + VFE_0_GLOBAL_RESET_CMD_BUS_BDG | + VFE_0_GLOBAL_RESET_CMD_BUS | + VFE_0_GLOBAL_RESET_CMD_CAMIF | + VFE_0_GLOBAL_RESET_CMD_CORE; + + writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); +} + +static void vfe_halt_request(struct vfe_device *vfe) +{ + writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, + vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_halt_clear(struct vfe_device *vfe) +{ + writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); +} + +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +static int vfe_word_per_line(u32 format, u32 pixel_per_line) +{ + int val = 0; + + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CALC_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CALC_WORD(pixel_per_line, 2, 8); + break; + } + + return val; +} + +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, + u16 *width, u16 *height, u16 *bytesperline) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + if (plane == 1) + *height /= 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + break; + } +} + +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable) +{ + u32 reg; + + if (enable) { + u16 width = 0, height = 0, bytesperline = 0, wpl; + + vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); + + wpl = vfe_word_per_line(pix->pixelformat, width); + + reg = height - 1; + reg |= ((wpl + 1) / 2 - 1) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + + wpl = vfe_word_per_line(pix->pixelformat, bytesperline); + + reg = 0x3; + reg |= (height - 1) << 4; + reg |= wpl << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } else { + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } +} + +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); + + reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); + + reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) + & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; + + writel_relaxed(reg, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); +} + +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, + u32 pattern) +{ + writel_relaxed(pattern, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); +} + +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, + u16 offset, u16 depth) +{ + u32 reg; + + reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | + depth; + writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); +} + +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) +{ + wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + wmb(); +} + +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); +} + +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); +} + +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); + + return (reg >> wm) & 0x1; +} + +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) +{ + if (enable) + writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); + else + writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); +} + +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & + VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) +{ + writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, + vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); +} + +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, + u8 enable) +{ + struct vfe_line *line = container_of(output, struct vfe_line, output); + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (i == 0) { + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + } else if (i == 1) { + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + } else { + /* On current devices output->wm_num is always <= 2 */ + break; + } + + if (output->wm_idx[i] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + } +} + +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) +{ + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), + VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); + + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), + cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); + wmb(); + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + wmb(); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); +} + +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | + VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | + VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } +} + +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable) +{ + struct vfe_output *output = &vfe->line[line_id].output; + unsigned int i; + u32 irq_en0; + u32 irq_en1; + u32 comp_mask = 0; + + irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; + irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; + irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); + irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; + for (i = 0; i < output->wm_num; i++) { + irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( + output->wm_idx[i]); + comp_mask |= (1 << output->wm_idx[i]) << comp * 8; + } + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; + u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | + VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; + + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); +} + +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val, even_cfg, odd_cfg; + + writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); + + val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); + + val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; + break; + } + + writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); + writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); +} + +static inline u8 vfe_calc_interp_reso(u16 input, u16 output) +{ + if (input / output >= 16) + return 0; + + if (input / output >= 8) + return 1; + + if (input / output >= 4) + return 2; + + return 3; +} + +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 input, output; + u8 interp_reso; + u32 phase_mult; + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) + output = line->compose.height / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); +} + +static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 first, last; + + first = line->crop.left; + last = line->crop.left + line->crop.width - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); + + first = line->crop.left / 2; + last = line->crop.left / 2 + line->crop.width / 2 - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { + first = line->crop.top / 2; + last = line->crop.top / 2 + line->crop.height / 2 - 1; + } + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); +} + +static void vfe_set_clamp_cfg(struct vfe_device *vfe) +{ + u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | + VFE_0_CLAMP_ENC_MAX_CFG_CH1 | + VFE_0_CLAMP_ENC_MAX_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); + + val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | + VFE_0_CLAMP_ENC_MIN_CFG_CH1 | + VFE_0_CLAMP_ENC_MIN_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) +{ + u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); + + if (enable) + vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); + else + vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); + + wmb(); +} + +static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val; + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; + break; + } + + writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2; + val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].height - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); + + val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); + + val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); +} + +static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable) +{ + u32 cmd; + + cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE; + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); + wmb(); + + if (enable) + cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY; + else + cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY; + + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); +} + +static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) +{ + u32 val = VFE_0_MODULE_CFG_DEMUX | + VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | + VFE_0_MODULE_CFG_SCALE_ENC | + VFE_0_MODULE_CFG_CROP_ENC; + + if (enable) + writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); + else + writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); +} + +static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, + val, + (val & VFE_0_CAMIF_STATUS_HALT), + CAMIF_TIMEOUT_SLEEP_US, + CAMIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(dev, "%s: camif stop timeout\n", __func__); + + return ret; +} + +static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) +{ + *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); + *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); + + writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0); + writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1); + + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); +} + +static void vfe_violation_read(struct vfe_device *vfe) +{ + u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); + + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); +} + +/* + * vfe_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 value0, value1; + int i, j; + + vfe->ops->isr_read(vfe, &value0, &value1); + + trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", + value0, value1); + + if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) + vfe->isr_ops.reset_ack(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) + vfe->ops->violation_read(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) + vfe->isr_ops.halt_ack(vfe); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) + if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) + vfe->isr_ops.reg_update(vfe, i); + + if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) + vfe->isr_ops.sof(vfe, VFE_LINE_PIX); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) + vfe->isr_ops.sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { + vfe->isr_ops.comp_done(vfe, i); + for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) + if (vfe->wm_output_map[j] == VFE_LINE_PIX) + value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); + } + + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) + vfe->isr_ops.wm_done(vfe, i); + + return IRQ_HANDLED; +} + +const struct vfe_hw_ops vfe_ops_4_1 = { + .hw_version_read = vfe_hw_version_read, + .get_ub_size = vfe_get_ub_size, + .global_reset = vfe_global_reset, + .halt_request = vfe_halt_request, + .halt_clear = vfe_halt_clear, + .wm_enable = vfe_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, + .bus_reload_wm = vfe_bus_reload_wm, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .wm_set_subsample = vfe_wm_set_subsample, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .set_xbar_cfg = vfe_set_xbar_cfg, + .set_rdi_cid = vfe_set_rdi_cid, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_common = vfe_enable_irq_common, + .set_demux_cfg = vfe_set_demux_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_qos = vfe_set_qos, + .set_cgc_override = vfe_set_cgc_override, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_module_cfg = vfe_set_module_cfg, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .isr_read = vfe_isr_read, + .violation_read = vfe_violation_read, + .isr = vfe_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 474e1dd02891..7f07a22c3e4b 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -33,194 +32,6 @@ #define to_vfe(ptr_line) \ container_of(vfe_line_array(ptr_line), struct vfe_device, line) -#define VFE_0_HW_VERSION 0x000 - -#define VFE_0_GLOBAL_RESET_CMD 0x00c -#define VFE_0_GLOBAL_RESET_CMD_CORE (1 << 0) -#define VFE_0_GLOBAL_RESET_CMD_CAMIF (1 << 1) -#define VFE_0_GLOBAL_RESET_CMD_BUS (1 << 2) -#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG (1 << 3) -#define VFE_0_GLOBAL_RESET_CMD_REGISTER (1 << 4) -#define VFE_0_GLOBAL_RESET_CMD_TIMER (1 << 5) -#define VFE_0_GLOBAL_RESET_CMD_PM (1 << 6) -#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR (1 << 7) -#define VFE_0_GLOBAL_RESET_CMD_TESTGEN (1 << 8) - -#define VFE_0_MODULE_CFG 0x018 -#define VFE_0_MODULE_CFG_DEMUX (1 << 2) -#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE (1 << 3) -#define VFE_0_MODULE_CFG_SCALE_ENC (1 << 23) -#define VFE_0_MODULE_CFG_CROP_ENC (1 << 27) - -#define VFE_0_CORE_CFG 0x01c -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 - -#define VFE_0_IRQ_CMD 0x024 -#define VFE_0_IRQ_CMD_GLOBAL_CLEAR (1 << 0) - -#define VFE_0_IRQ_MASK_0 0x028 -#define VFE_0_IRQ_MASK_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_MASK_0_CAMIF_EOF (1 << 1) -#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_MASK_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_MASK_1 0x02c -#define VFE_0_IRQ_MASK_1_CAMIF_ERROR (1 << 0) -#define VFE_0_IRQ_MASK_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) (1 << ((n) + 9)) -#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_CLEAR_0 0x030 -#define VFE_0_IRQ_CLEAR_1 0x034 - -#define VFE_0_IRQ_STATUS_0 0x038 -#define VFE_0_IRQ_STATUS_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_STATUS_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_STATUS_1 0x03c -#define VFE_0_IRQ_STATUS_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 -#define VFE_0_VIOLATION_STATUS 0x48 - -#define VFE_0_BUS_CMD 0x4c -#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) (1 << (x)) - -#define VFE_0_BUS_CFG 0x050 - -#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN (1 << 1) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1F << 2) - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ - (0x088 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ - (0x08c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff - -#define VFE_0_BUS_PING_PONG_STATUS 0x268 - -#define VFE_0_BUS_BDG_CMD 0x2c0 -#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 - -#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 -#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 -#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 -#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc -#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 -#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 -#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 -#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc -#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 -#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 - -#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) -#define VFE_0_RDI_CFG_x_RDI_EN_BIT (1 << 2) -#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 -#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) (1 << (16 + (r))) - -#define VFE_0_CAMIF_CMD 0x2f4 -#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 -#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 -#define VFE_0_CAMIF_CMD_NO_CHANGE 3 -#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2) -#define VFE_0_CAMIF_CFG 0x2f8 -#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6) -#define VFE_0_CAMIF_FRAME_CFG 0x300 -#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 -#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 -#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c -#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 -#define VFE_0_CAMIF_STATUS 0x31c -#define VFE_0_CAMIF_STATUS_HALT (1 << 31) - -#define VFE_0_REG_UPDATE 0x378 -#define VFE_0_REG_UPDATE_RDIn(n) (1 << (1 + (n))) -#define VFE_0_REG_UPDATE_line_n(n) \ - ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) - -#define VFE_0_DEMUX_CFG 0x424 -#define VFE_0_DEMUX_CFG_PERIOD 0x3 -#define VFE_0_DEMUX_GAIN_0 0x428 -#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) -#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) -#define VFE_0_DEMUX_GAIN_1 0x42c -#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) -#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) -#define VFE_0_DEMUX_EVEN_CFG 0x438 -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 -#define VFE_0_DEMUX_ODD_CFG 0x43c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 - -#define VFE_0_SCALE_ENC_Y_CFG 0x75c -#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 -#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 -#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c -#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 -#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 -#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c -#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 -#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 -#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 - -#define VFE_0_CROP_ENC_Y_WIDTH 0x854 -#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 -#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c -#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 - -#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 -#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) -#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 -#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) - -#define VFE_0_CGC_OVERRIDE_1 0x974 -#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) (1 << (x)) - /* VFE reset timeout */ #define VFE_RESET_TIMEOUT_MS 50 /* VFE halt timeout */ @@ -232,9 +43,6 @@ #define VFE_NEXT_SOF_MS 500 -#define CAMIF_TIMEOUT_SLEEP_US 1000 -#define CAMIF_TIMEOUT_ALL_US 1000000 - #define SCALER_RATIO_MAX 16 static const struct { @@ -326,541 +134,6 @@ static u8 vfe_get_bpp(u32 code) return vfe_formats[0].bpp; } -static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits & ~clr_bits, vfe->base + reg); -} - -static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits | set_bits, vfe->base + reg); -} - -static void vfe_global_reset(struct vfe_device *vfe) -{ - u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | - VFE_0_GLOBAL_RESET_CMD_BUS_MISR | - VFE_0_GLOBAL_RESET_CMD_PM | - VFE_0_GLOBAL_RESET_CMD_TIMER | - VFE_0_GLOBAL_RESET_CMD_REGISTER | - VFE_0_GLOBAL_RESET_CMD_BUS_BDG | - VFE_0_GLOBAL_RESET_CMD_BUS | - VFE_0_GLOBAL_RESET_CMD_CAMIF | - VFE_0_GLOBAL_RESET_CMD_CORE; - - writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); -} - -static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); -} - -static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); -} - -#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) - -static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line) -{ - int val = 0; - - switch (format) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - val = CALC_WORD(pixel_per_line, 1, 8); - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - val = CALC_WORD(pixel_per_line, 2, 8); - break; - } - - return val; -} - -static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, - u16 *width, u16 *height, u16 *bytesperline) -{ - switch (pix->pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - if (plane == 1) - *height /= 2; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - break; - } -} - -static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, - struct v4l2_pix_format_mplane *pix, - u8 plane, u32 enable) -{ - u32 reg; - - if (enable) { - u16 width = 0, height = 0, bytesperline = 0, wpl; - - vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); - - wpl = vfe_word_per_line(pix->pixelformat, width); - - reg = height - 1; - reg |= ((wpl + 1) / 2 - 1) << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - - wpl = vfe_word_per_line(pix->pixelformat, bytesperline); - - reg = 0x3; - reg |= (height - 1) << 4; - reg |= wpl << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } else { - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } -} - -static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); - - reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); - - reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) - & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; - - writel_relaxed(reg, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); -} - -static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, - u32 pattern) -{ - writel_relaxed(pattern, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); -} - -static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset, - u16 depth) -{ - u32 reg; - - reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | - depth; - writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); -} - -static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) -{ - wmb(); - writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); - wmb(); -} - -static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); -} - -static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); -} - -static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); - - return (reg >> wm) & 0x1; -} - -static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) -{ - if (enable) - writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); - else - writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); -} - -static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & - VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} - -static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) -{ - writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, - vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); -} - -static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} - -static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, - u8 enable) -{ - struct vfe_line *line = container_of(output, struct vfe_line, output); - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (i == 0) { - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - } else if (i == 1) { - reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) - reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; - } else { - /* On current devices output->wm_num is always <= 2 */ - break; - } - - if (output->wm_idx[i] % 2 == 1) - reg <<= 16; - - if (enable) - vfe_reg_set(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - else - vfe_reg_clr(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - } -} - -static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) -{ - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), - VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); - - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), - cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); -} - -static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); - wmb(); - writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); - wmb(); -} - -static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, - enum vfe_line_id line_id, u8 enable) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | - VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | - VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } -} - -static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, - enum vfe_line_id line_id, u8 enable) -{ - struct vfe_output *output = &vfe->line[line_id].output; - unsigned int i; - u32 irq_en0; - u32 irq_en1; - u32 comp_mask = 0; - - irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; - irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; - irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); - irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; - for (i = 0; i < output->wm_num; i++) { - irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( - output->wm_idx[i]); - comp_mask |= (1 << output->wm_idx[i]) << comp * 8; - } - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } -} - -static void vfe_enable_irq_common(struct vfe_device *vfe) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; - u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | - VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; - - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); -} - -static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val, even_cfg, odd_cfg; - - writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); - - val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); - - val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); - - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; - break; - } - - writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); - writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); -} - -static inline u8 vfe_calc_interp_reso(u16 input, u16 output) -{ - if (input / output >= 16) - return 0; - - if (input / output >= 8) - return 1; - - if (input / output >= 4) - return 2; - - return 3; -} - -static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 input, output; - u8 interp_reso; - u32 phase_mult; - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) - output = line->compose.height / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); -} - -static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 first, last; - - first = line->crop.left; - last = line->crop.left + line->crop.width - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); - - first = line->crop.left / 2; - last = line->crop.left / 2 + line->crop.width / 2 - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { - first = line->crop.top / 2; - last = line->crop.top / 2 + line->crop.height / 2 - 1; - } - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); -} - -static void vfe_set_clamp_cfg(struct vfe_device *vfe) -{ - u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | - VFE_0_CLAMP_ENC_MAX_CFG_CH1 | - VFE_0_CLAMP_ENC_MAX_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); - - val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | - VFE_0_CLAMP_ENC_MIN_CFG_CH1 | - VFE_0_CLAMP_ENC_MIN_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); -} - /* * vfe_reset - Trigger reset on VFE module and wait to complete * @vfe: VFE device @@ -873,7 +146,7 @@ static int vfe_reset(struct vfe_device *vfe) reinit_completion(&vfe->reset_complete); - vfe_global_reset(vfe); + vfe->ops->global_reset(vfe); time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); @@ -897,8 +170,7 @@ static int vfe_halt(struct vfe_device *vfe) reinit_completion(&vfe->halt_complete); - writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, - vfe->base + VFE_0_BUS_BDG_CMD); + vfe->ops->halt_request(vfe); time = wait_for_completion_timeout(&vfe->halt_complete, msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); @@ -936,117 +208,6 @@ static void vfe_reset_output_maps(struct vfe_device *vfe) vfe->wm_output_map[i] = VFE_LINE_NONE; } -static void vfe_set_qos(struct vfe_device *vfe) -{ - u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; - u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; - - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); - writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); -} - -static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) -{ - u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); - - if (enable) - vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); - else - vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); - - wmb(); -} - -static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) -{ - u32 val = VFE_0_MODULE_CFG_DEMUX | - VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | - VFE_0_MODULE_CFG_SCALE_ENC | - VFE_0_MODULE_CFG_CROP_ENC; - - if (enable) - writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); - else - writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); -} - -static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val; - - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; - break; - } - - writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2; - val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].height - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); - - val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); - - val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); -} - -static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd) -{ - writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | - VFE_0_CAMIF_CMD_NO_CHANGE, - vfe->base + VFE_0_CAMIF_CMD); - wmb(); - - writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); -} - -static int vfe_camif_wait_for_stop(struct vfe_device *vfe) -{ - u32 val; - int ret; - - ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, - val, - (val & VFE_0_CAMIF_STATUS_HALT), - CAMIF_TIMEOUT_SLEEP_US, - CAMIF_TIMEOUT_ALL_US); - if (ret < 0) - dev_err(vfe->camss->dev, "%s: camif stop timeout\n", __func__); - - return ret; -} - static void vfe_output_init_addrs(struct vfe_device *vfe, struct vfe_output *output, u8 sync) { @@ -1067,10 +228,10 @@ static void vfe_output_init_addrs(struct vfe_device *vfe, else pong_addr = ping_addr; - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1086,9 +247,9 @@ static void vfe_output_update_ping_addr(struct vfe_device *vfe, else addr = 0; - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1104,9 +265,9 @@ static void vfe_output_update_pong_addr(struct vfe_device *vfe, else addr = 0; - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1150,12 +311,13 @@ static void vfe_output_frame_drop(struct vfe_device *vfe, drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; for (i = 0; i < output->wm_num; i++) { - vfe_wm_set_framedrop_period(vfe, output->wm_idx[i], - drop_period); - vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i], - drop_pattern); + vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i], + drop_period); + vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i], + drop_pattern); } - vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id); + vfe->ops->reg_update(vfe, + container_of(output, struct vfe_line, output)->id); } static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) @@ -1352,24 +514,18 @@ static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; + const struct vfe_hw_ops *ops = vfe->ops; unsigned long flags; unsigned int i; u16 ub_size; - switch (vfe->id) { - case 0: - ub_size = MSM_VFE_VFE0_UB_SIZE_RDI; - break; - case 1: - ub_size = MSM_VFE_VFE1_UB_SIZE_RDI; - break; - default: + ub_size = ops->get_ub_size(vfe->id); + if (!ub_size) return -EINVAL; - } spin_lock_irqsave(&vfe->output_lock, flags); - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id); + ops->reg_update_clear(vfe, line->id); if (output->state != VFE_OUTPUT_RESERVED) { dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", @@ -1414,42 +570,42 @@ static int vfe_enable_output(struct vfe_line *line) vfe_output_init_addrs(vfe, output, 0); if (line->id != VFE_LINE_PIX) { - vfe_set_cgc_override(vfe, output->wm_idx[0], 1); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); - vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); - vfe_wm_set_subsample(vfe, output->wm_idx[0]); - vfe_set_rdi_cid(vfe, line->id, 0); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[0], - (ub_size + 1) * output->wm_idx[0], ub_size); - vfe_wm_frame_based(vfe, output->wm_idx[0], 1); - vfe_wm_enable(vfe, output->wm_idx[0], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[0]); + ops->set_cgc_override(vfe, output->wm_idx[0], 1); + ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); + ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); + ops->wm_set_subsample(vfe, output->wm_idx[0]); + ops->set_rdi_cid(vfe, line->id, 0); + ops->wm_set_ub_cfg(vfe, output->wm_idx[0], + (ub_size + 1) * output->wm_idx[0], ub_size); + ops->wm_frame_based(vfe, output->wm_idx[0], 1); + ops->wm_enable(vfe, output->wm_idx[0], 1); + ops->bus_reload_wm(vfe, output->wm_idx[0]); } else { ub_size /= output->wm_num; for (i = 0; i < output->wm_num; i++) { - vfe_set_cgc_override(vfe, output->wm_idx[i], 1); - vfe_wm_set_subsample(vfe, output->wm_idx[i]); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[i], - (ub_size + 1) * output->wm_idx[i], - ub_size); - vfe_wm_line_based(vfe, output->wm_idx[i], + ops->set_cgc_override(vfe, output->wm_idx[i], 1); + ops->wm_set_subsample(vfe, output->wm_idx[i]); + ops->wm_set_ub_cfg(vfe, output->wm_idx[i], + (ub_size + 1) * output->wm_idx[i], + ub_size); + ops->wm_line_based(vfe, output->wm_idx[i], &line->video_out.active_fmt.fmt.pix_mp, i, 1); - vfe_wm_enable(vfe, output->wm_idx[i], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + ops->wm_enable(vfe, output->wm_idx[i], 1); + ops->bus_reload_wm(vfe, output->wm_idx[i]); } - vfe_enable_irq_pix_line(vfe, 0, line->id, 1); - vfe_set_module_cfg(vfe, 1); - vfe_set_camif_cfg(vfe, line); - vfe_set_xbar_cfg(vfe, output, 1); - vfe_set_demux_cfg(vfe, line); - vfe_set_scale_cfg(vfe, line); - vfe_set_crop_cfg(vfe, line); - vfe_set_clamp_cfg(vfe); - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY); + ops->enable_irq_pix_line(vfe, 0, line->id, 1); + ops->set_module_cfg(vfe, 1); + ops->set_camif_cfg(vfe, line); + ops->set_xbar_cfg(vfe, output, 1); + ops->set_demux_cfg(vfe, line); + ops->set_scale_cfg(vfe, line); + ops->set_crop_cfg(vfe, line); + ops->set_clamp_cfg(vfe); + ops->set_camif_cmd(vfe, 1); } - vfe_reg_update(vfe, line->id); + ops->reg_update(vfe, line->id); spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -1460,6 +616,7 @@ static int vfe_disable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; + const struct vfe_hw_ops *ops = vfe->ops; unsigned long flags; unsigned long time; unsigned int i; @@ -1476,9 +633,9 @@ static int vfe_disable_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) - vfe_wm_enable(vfe, output->wm_idx[i], 0); + ops->wm_enable(vfe, output->wm_idx[i], 0); - vfe_reg_update(vfe, line->id); + ops->reg_update(vfe, line->id); output->wait_reg_update = 1; spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -1490,25 +647,26 @@ static int vfe_disable_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); if (line->id != VFE_LINE_PIX) { - vfe_wm_frame_based(vfe, output->wm_idx[0], 0); - vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); - vfe_set_cgc_override(vfe, output->wm_idx[0], 0); + ops->wm_frame_based(vfe, output->wm_idx[0], 0); + ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], + line->id); + ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); + ops->set_cgc_override(vfe, output->wm_idx[0], 0); spin_unlock_irqrestore(&vfe->output_lock, flags); } else { for (i = 0; i < output->wm_num; i++) { - vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); - vfe_set_cgc_override(vfe, output->wm_idx[i], 0); + ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); + ops->set_cgc_override(vfe, output->wm_idx[i], 0); } - vfe_enable_irq_pix_line(vfe, 0, line->id, 0); - vfe_set_module_cfg(vfe, 0); - vfe_set_xbar_cfg(vfe, output, 0); + ops->enable_irq_pix_line(vfe, 0, line->id, 0); + ops->set_module_cfg(vfe, 0); + ops->set_xbar_cfg(vfe, output, 0); - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY); + ops->set_camif_cmd(vfe, 0); spin_unlock_irqrestore(&vfe->output_lock, flags); - vfe_camif_wait_for_stop(vfe); + ops->camif_wait_for_stop(vfe, vfe->camss->dev); } return 0; @@ -1528,11 +686,11 @@ static int vfe_enable(struct vfe_line *line) mutex_lock(&vfe->stream_lock); if (!vfe->stream_count) { - vfe_enable_irq_common(vfe); + vfe->ops->enable_irq_common(vfe); - vfe_bus_enable_wr_if(vfe, 1); + vfe->ops->bus_enable_wr_if(vfe, 1); - vfe_set_qos(vfe); + vfe->ops->set_qos(vfe); } vfe->stream_count++; @@ -1559,7 +717,7 @@ error_get_output: mutex_lock(&vfe->stream_lock); if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); + vfe->ops->bus_enable_wr_if(vfe, 0); vfe->stream_count--; @@ -1585,7 +743,7 @@ static int vfe_disable(struct vfe_line *line) mutex_lock(&vfe->stream_lock); if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); + vfe->ops->bus_enable_wr_if(vfe, 0); vfe->stream_count--; @@ -1624,7 +782,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); + vfe->ops->reg_update_clear(vfe, line_id); output = &vfe->line[line_id].output; @@ -1694,7 +852,7 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) u64 ts = ktime_get_ns(); unsigned int i; - active_index = vfe_wm_get_ping_pong_status(vfe, wm); + active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm); spin_lock_irqsave(&vfe->output_lock, flags); @@ -1736,12 +894,12 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) if (active_index) for (i = 0; i < output->wm_num; i++) - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], - new_addr[i]); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], + new_addr[i]); else for (i = 0; i < output->wm_num; i++) - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], - new_addr[i]); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], + new_addr[i]); spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -1772,67 +930,15 @@ static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) } } -/* - * vfe_isr - ISPIF module interrupt handler - * @irq: Interrupt line - * @dev: VFE device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t vfe_isr(int irq, void *dev) +static inline void vfe_isr_reset_ack(struct vfe_device *vfe) { - struct vfe_device *vfe = dev; - u32 value0, value1; - u32 violation; - int i, j; - - value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); - value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); - - writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0); - writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1); - - wmb(); - writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); - - if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) - complete(&vfe->reset_complete); - - if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) { - violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); - dev_err_ratelimited(vfe->camss->dev, - "VFE: violation = 0x%08x\n", violation); - } - - if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) { - complete(&vfe->halt_complete); - writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); - } - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) - if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) - vfe_isr_reg_update(vfe, i); - - if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) - vfe_isr_sof(vfe, VFE_LINE_PIX); - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) - if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) - vfe_isr_sof(vfe, i); - - for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { - vfe_isr_comp_done(vfe, i); - for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) - if (vfe->wm_output_map[j] == VFE_LINE_PIX) - value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); - } - - for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) - vfe_isr_wm_done(vfe, i); + complete(&vfe->reset_complete); +} - return IRQ_HANDLED; +static inline void vfe_isr_halt_ack(struct vfe_device *vfe) +{ + complete(&vfe->halt_complete); + vfe->ops->halt_clear(vfe); } /* @@ -2142,15 +1248,11 @@ static int vfe_set_power(struct v4l2_subdev *sd, int on) int ret; if (on) { - u32 hw_version; - ret = vfe_get(vfe); if (ret < 0) return ret; - hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); - dev_dbg(vfe->camss->dev, - "VFE HW Version = 0x%08x\n", hw_version); + vfe->ops->hw_version_read(vfe, vfe->camss->dev); } else { vfe_put(vfe); } @@ -2744,6 +1846,18 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, int i, j; int ret; + vfe->isr_ops.reset_ack = vfe_isr_reset_ack; + vfe->isr_ops.halt_ack = vfe_isr_halt_ack; + vfe->isr_ops.reg_update = vfe_isr_reg_update; + vfe->isr_ops.sof = vfe_isr_sof; + vfe->isr_ops.comp_done = vfe_isr_comp_done; + vfe->isr_ops.wm_done = vfe_isr_wm_done; + + if (camss->version == CAMSS_8x16) + vfe->ops = &vfe_ops_4_1; + else + return -EINVAL; + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -2765,7 +1879,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->irq = r->start; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", dev_name(dev), MSM_VFE_NAME, vfe->id); - ret = devm_request_irq(dev, vfe->irq, vfe_isr, + ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 17d431e563da..19041ba6644a 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -26,11 +26,6 @@ #define MSM_VFE_IMAGE_MASTERS_NUM 7 #define MSM_VFE_COMPOSITE_IRQ_NUM 4 -#define MSM_VFE_VFE0_UB_SIZE 1023 -#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) -#define MSM_VFE_VFE1_UB_SIZE 1535 -#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) - enum vfe_output_state { VFE_OUTPUT_OFF, VFE_OUTPUT_RESERVED, @@ -78,6 +73,70 @@ struct vfe_line { struct vfe_output output; }; +struct vfe_device; + +struct vfe_hw_ops { + void (*hw_version_read)(struct vfe_device *vfe, struct device *dev); + u16 (*get_ub_size)(u8 vfe_id); + void (*global_reset)(struct vfe_device *vfe); + void (*halt_request)(struct vfe_device *vfe); + void (*halt_clear)(struct vfe_device *vfe); + void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*wm_line_based)(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable); + void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per); + void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm, + u32 pattern); + void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset, + u16 depth); + void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm); + void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr); + void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr); + int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm); + void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable); + void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id); + void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm); + void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id); + void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output, + u8 enable); + void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id, + u8 cid); + void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*reg_update_clear)(struct vfe_device *vfe, + enum vfe_line_id line_id); + void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable); + void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable); + void (*enable_irq_common)(struct vfe_device *vfe); + void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_clamp_cfg)(struct vfe_device *vfe); + void (*set_qos)(struct vfe_device *vfe); + void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable); + void (*set_module_cfg)(struct vfe_device *vfe, u8 enable); + int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev); + void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1); + void (*violation_read)(struct vfe_device *vfe); + irqreturn_t (*isr)(int irq, void *dev); +}; + +struct vfe_isr_ops { + void (*reset_ack)(struct vfe_device *vfe); + void (*halt_ack)(struct vfe_device *vfe); + void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*sof)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*comp_done)(struct vfe_device *vfe, u8 comp); + void (*wm_done)(struct vfe_device *vfe, u8 wm); +}; + struct vfe_device { struct camss *camss; u8 id; @@ -97,6 +156,8 @@ struct vfe_device { struct vfe_line line[MSM_VFE_LINE_NUM]; u32 reg_update; u8 was_streaming; + const struct vfe_hw_ops *ops; + struct vfe_isr_ops isr_ops; }; struct resources; @@ -114,4 +175,6 @@ void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); void msm_vfe_stop_streaming(struct vfe_device *vfe); +extern const struct vfe_hw_ops vfe_ops_4_1; + #endif /* QC_MSM_CAMSS_VFE_H */ -- cgit v1.2.3-70-g09d2 From 4e1abf66feba454b1dc3d4c7d86759346ec1f134 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:34 -0400 Subject: media: camss: vfe: Add support for 8x96 Add VFE hardware dependent part for 8x96. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-vfe-4-1.c | 6 + drivers/media/platform/qcom/camss/camss-vfe-4-7.c | 1051 +++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-vfe.c | 4 + drivers/media/platform/qcom/camss/camss-vfe.h | 2 + 5 files changed, 1064 insertions(+) create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-4-7.c (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 38dc56e1630c..f5e6e255f2a1 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -8,6 +8,7 @@ qcom-camss-objs += \ camss-csiphy.o \ camss-ispif.o \ camss-vfe-4-1.o \ + camss-vfe-4-7.o \ camss-vfe.o \ camss-video.o \ diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 070c0c381f1b..41184dcc33ca 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -789,6 +789,11 @@ static void vfe_set_qos(struct vfe_device *vfe) writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); } +static void vfe_set_ds(struct vfe_device *vfe) +{ + /* empty */ +} + static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) { u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); @@ -995,6 +1000,7 @@ const struct vfe_hw_ops vfe_ops_4_1 = { .set_crop_cfg = vfe_set_crop_cfg, .set_clamp_cfg = vfe_set_clamp_cfg, .set_qos = vfe_set_qos, + .set_ds = vfe_set_ds, .set_cgc_override = vfe_set_cgc_override, .set_camif_cfg = vfe_set_camif_cfg, .set_camif_cmd = vfe_set_camif_cmd, diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c new file mode 100644 index 000000000000..45e6711a346f --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-4-7.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.7 + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + */ + +#include +#include + +#include "camss-vfe.h" + +#define VFE_0_HW_VERSION 0x000 + +#define VFE_0_GLOBAL_RESET_CMD 0x018 +#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0) +#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1) +#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2) +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3) +#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4) +#define VFE_0_GLOBAL_RESET_CMD_PM BIT(5) +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(6) +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(7) +#define VFE_0_GLOBAL_RESET_CMD_DSP BIT(8) +#define VFE_0_GLOBAL_RESET_CMD_IDLE_CGC BIT(9) + +#define VFE_0_MODULE_LENS_EN 0x040 +#define VFE_0_MODULE_LENS_EN_DEMUX BIT(2) +#define VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE BIT(3) + +#define VFE_0_MODULE_ZOOM_EN 0x04c +#define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1) +#define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2) + +#define VFE_0_CORE_CFG 0x050 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 +#define VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN BIT(4) + +#define VFE_0_IRQ_CMD 0x058 +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_0_IRQ_MASK_0 0x05c +#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1) +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_MASK_1 0x060 +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0) +#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7) +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9) +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_CLEAR_0 0x064 +#define VFE_0_IRQ_CLEAR_1 0x068 + +#define VFE_0_IRQ_STATUS_0 0x06c +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_STATUS_1 0x070 +#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7) +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_COMPOSITE_MASK_0 0x074 +#define VFE_0_VIOLATION_STATUS 0x07c + +#define VFE_0_BUS_CMD 0x80 +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x) + +#define VFE_0_BUS_CFG 0x084 + +#define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2)) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 0xc +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 0xd +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 0xe + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x0a0 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x0a4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x0ac + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x0b4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT 1 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x0b8 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x0bc + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x0c0 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ + (0x0c4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ + (0x0c8 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff + +#define VFE_0_BUS_PING_PONG_STATUS 0x338 + +#define VFE_0_BUS_BDG_CMD 0x400 +#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 + +#define VFE_0_BUS_BDG_QOS_CFG_0 0x404 +#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa9aaa9 +#define VFE_0_BUS_BDG_QOS_CFG_1 0x408 +#define VFE_0_BUS_BDG_QOS_CFG_2 0x40c +#define VFE_0_BUS_BDG_QOS_CFG_3 0x410 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x414 +#define VFE_0_BUS_BDG_QOS_CFG_5 0x418 +#define VFE_0_BUS_BDG_QOS_CFG_6 0x41c +#define VFE_0_BUS_BDG_QOS_CFG_7 0x420 +#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa9 + +#define VFE_0_BUS_BDG_DS_CFG_0 0x424 +#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc0011 +#define VFE_0_BUS_BDG_DS_CFG_1 0x428 +#define VFE_0_BUS_BDG_DS_CFG_2 0x42c +#define VFE_0_BUS_BDG_DS_CFG_3 0x430 +#define VFE_0_BUS_BDG_DS_CFG_4 0x434 +#define VFE_0_BUS_BDG_DS_CFG_5 0x438 +#define VFE_0_BUS_BDG_DS_CFG_6 0x43c +#define VFE_0_BUS_BDG_DS_CFG_7 0x440 +#define VFE_0_BUS_BDG_DS_CFG_8 0x444 +#define VFE_0_BUS_BDG_DS_CFG_9 0x448 +#define VFE_0_BUS_BDG_DS_CFG_10 0x44c +#define VFE_0_BUS_BDG_DS_CFG_11 0x450 +#define VFE_0_BUS_BDG_DS_CFG_12 0x454 +#define VFE_0_BUS_BDG_DS_CFG_13 0x458 +#define VFE_0_BUS_BDG_DS_CFG_14 0x45c +#define VFE_0_BUS_BDG_DS_CFG_15 0x460 +#define VFE_0_BUS_BDG_DS_CFG_16 0x464 +#define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x40000103 + +#define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x))) +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) +#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2) +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 + +#define VFE_0_CAMIF_CMD 0x478 +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_NO_CHANGE 3 +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2) +#define VFE_0_CAMIF_CFG 0x47c +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6) +#define VFE_0_CAMIF_FRAME_CFG 0x484 +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x488 +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x48c +#define VFE_0_CAMIF_SUBSAMPLE_CFG 0x490 +#define VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN 0x498 +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x49c +#define VFE_0_CAMIF_STATUS 0x4a4 +#define VFE_0_CAMIF_STATUS_HALT BIT(31) + +#define VFE_0_REG_UPDATE 0x4ac +#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n)) +#define VFE_0_REG_UPDATE_line_n(n) \ + ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) + +#define VFE_0_DEMUX_CFG 0x560 +#define VFE_0_DEMUX_CFG_PERIOD 0x3 +#define VFE_0_DEMUX_GAIN_0 0x564 +#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) +#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) +#define VFE_0_DEMUX_GAIN_1 0x568 +#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) +#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) +#define VFE_0_DEMUX_EVEN_CFG 0x574 +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 +#define VFE_0_DEMUX_ODD_CFG 0x578 +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 + +#define VFE_0_SCALE_ENC_Y_CFG 0x91c +#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x920 +#define VFE_0_SCALE_ENC_Y_H_PHASE 0x924 +#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x934 +#define VFE_0_SCALE_ENC_Y_V_PHASE 0x938 +#define VFE_0_SCALE_ENC_CBCR_CFG 0x948 +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x94c +#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x950 +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x960 +#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x964 + +#define VFE_0_CROP_ENC_Y_WIDTH 0x974 +#define VFE_0_CROP_ENC_Y_HEIGHT 0x978 +#define VFE_0_CROP_ENC_CBCR_WIDTH 0x97c +#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x980 + +#define VFE_0_CLAMP_ENC_MAX_CFG 0x984 +#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) +#define VFE_0_CLAMP_ENC_MIN_CFG 0x988 +#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) + +#define CAMIF_TIMEOUT_SLEEP_US 1000 +#define CAMIF_TIMEOUT_ALL_US 1000000 + +#define MSM_VFE_VFE0_UB_SIZE 2047 +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) +#define MSM_VFE_VFE1_UB_SIZE 1535 +#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) + +static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); + + dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version); +} + +static u16 vfe_get_ub_size(u8 vfe_id) +{ + if (vfe_id == 0) + return MSM_VFE_VFE0_UB_SIZE_RDI; + else if (vfe_id == 1) + return MSM_VFE_VFE1_UB_SIZE_RDI; + + return 0; +} + +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits & ~clr_bits, vfe->base + reg); +} + +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits | set_bits, vfe->base + reg); +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_IDLE_CGC | + VFE_0_GLOBAL_RESET_CMD_DSP | + VFE_0_GLOBAL_RESET_CMD_TESTGEN | + VFE_0_GLOBAL_RESET_CMD_BUS_MISR | + VFE_0_GLOBAL_RESET_CMD_PM | + VFE_0_GLOBAL_RESET_CMD_REGISTER | + VFE_0_GLOBAL_RESET_CMD_BUS_BDG | + VFE_0_GLOBAL_RESET_CMD_BUS | + VFE_0_GLOBAL_RESET_CMD_CAMIF | + VFE_0_GLOBAL_RESET_CMD_CORE; + + writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0); + wmb(); + writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); +} + +static void vfe_halt_request(struct vfe_device *vfe) +{ + writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, + vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_halt_clear(struct vfe_device *vfe) +{ + writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); +} + +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT); +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +static int vfe_word_per_line(u32 format, u32 pixel_per_line) +{ + int val = 0; + + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CALC_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CALC_WORD(pixel_per_line, 2, 8); + break; + } + + return val; +} + +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, + u16 *width, u16 *height, u16 *bytesperline) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + if (plane == 1) + *height /= 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + break; + } +} + +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable) +{ + u32 reg; + + if (enable) { + u16 width = 0, height = 0, bytesperline = 0, wpl; + + vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); + + wpl = vfe_word_per_line(pix->pixelformat, width); + + reg = height - 1; + reg |= ((wpl + 3) / 4 - 1) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + + wpl = vfe_word_per_line(pix->pixelformat, bytesperline); + + reg = 0x3; + reg |= (height - 1) << 2; + reg |= ((wpl + 1) / 2) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } else { + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } +} + +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); + + reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); + + reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) + & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; + + writel_relaxed(reg, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); +} + +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, + u32 pattern) +{ + writel_relaxed(pattern, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); +} + +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, + u16 offset, u16 depth) +{ + u32 reg; + + reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | + depth; + writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); +} + +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) +{ + wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + wmb(); +} + +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); +} + +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); +} + +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); + + return (reg >> wm) & 0x1; +} + +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) +{ + if (enable) + writel_relaxed(0x101, vfe->base + VFE_0_BUS_CFG); + else + writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); +} + +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & + VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) +{ + writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, + vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); +} + +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, + u8 enable) +{ + struct vfe_line *line = container_of(output, struct vfe_line, output); + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (i == 0) { + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + } else if (i == 1) { + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + } + + if (output->wm_idx[i] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + } +} + +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) +{ + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), + VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); + + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), + cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); + wmb(); + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + wmb(); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); +} + +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | + VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | + VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } +} + +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable) +{ + struct vfe_output *output = &vfe->line[line_id].output; + unsigned int i; + u32 irq_en0; + u32 irq_en1; + u32 comp_mask = 0; + + irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; + irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; + irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); + irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; + for (i = 0; i < output->wm_num; i++) { + irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( + output->wm_idx[i]); + comp_mask |= (1 << output->wm_idx[i]) << comp * 8; + } + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; + u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | + VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; + + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); +} + +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val, even_cfg, odd_cfg; + + writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); + + val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); + + val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; + break; + } + + writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); + writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); +} + +static inline u8 vfe_calc_interp_reso(u16 input, u16 output) +{ + if (input / output >= 16) + return 0; + + if (input / output >= 8) + return 1; + + if (input / output >= 4) + return 2; + + return 3; +} + +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 input, output; + u8 interp_reso; + u32 phase_mult; + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width - 1; + output = line->compose.width - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height - 1; + output = line->compose.height - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width - 1; + output = line->compose.width / 2 - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height - 1; + output = line->compose.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) + output = line->compose.height / 2 - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); +} + +static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 first, last; + + first = line->crop.left; + last = line->crop.left + line->crop.width - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); + + first = line->crop.left / 2; + last = line->crop.left / 2 + line->crop.width / 2 - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { + first = line->crop.top / 2; + last = line->crop.top / 2 + line->crop.height / 2 - 1; + } + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); +} + +static void vfe_set_clamp_cfg(struct vfe_device *vfe) +{ + u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | + VFE_0_CLAMP_ENC_MAX_CFG_CH1 | + VFE_0_CLAMP_ENC_MAX_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); + + val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | + VFE_0_CLAMP_ENC_MIN_CFG_CH1 | + VFE_0_CLAMP_ENC_MIN_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe_set_ds(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG; + u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15); + writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16); +} + +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) +{ + /* empty */ +} + +static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val; + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; + break; + } + + val |= VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN; + writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + val |= (line->fmt[MSM_VFE_PAD_SINK].height - 1) << 16; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].height - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); + + val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); + + val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); +} + +static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable) +{ + u32 cmd; + + cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE; + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); + wmb(); + + if (enable) + cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY; + else + cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY; + + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); +} + +static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) +{ + u32 val_lens = VFE_0_MODULE_LENS_EN_DEMUX | + VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE; + u32 val_zoom = VFE_0_MODULE_ZOOM_EN_SCALE_ENC | + VFE_0_MODULE_ZOOM_EN_CROP_ENC; + + if (enable) { + writel_relaxed(val_lens, vfe->base + VFE_0_MODULE_LENS_EN); + writel_relaxed(val_zoom, vfe->base + VFE_0_MODULE_ZOOM_EN); + } else { + writel_relaxed(0x0, vfe->base + VFE_0_MODULE_LENS_EN); + writel_relaxed(0x0, vfe->base + VFE_0_MODULE_ZOOM_EN); + } +} + +static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, + val, + (val & VFE_0_CAMIF_STATUS_HALT), + CAMIF_TIMEOUT_SLEEP_US, + CAMIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(dev, "%s: camif stop timeout\n", __func__); + + return ret; +} + +static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) +{ + *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); + *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); + + writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0); + writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1); + + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); +} + +static void vfe_violation_read(struct vfe_device *vfe) +{ + u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); + + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); +} + +/* + * vfe_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 value0, value1; + int i, j; + + vfe->ops->isr_read(vfe, &value0, &value1); + + trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", + value0, value1); + + if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) + vfe->isr_ops.reset_ack(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) + vfe->ops->violation_read(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) + vfe->isr_ops.halt_ack(vfe); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) + if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) + vfe->isr_ops.reg_update(vfe, i); + + if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) + vfe->isr_ops.sof(vfe, VFE_LINE_PIX); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) + vfe->isr_ops.sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { + vfe->isr_ops.comp_done(vfe, i); + for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) + if (vfe->wm_output_map[j] == VFE_LINE_PIX) + value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); + } + + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) + vfe->isr_ops.wm_done(vfe, i); + + return IRQ_HANDLED; +} + +const struct vfe_hw_ops vfe_ops_4_7 = { + .hw_version_read = vfe_hw_version_read, + .get_ub_size = vfe_get_ub_size, + .global_reset = vfe_global_reset, + .halt_request = vfe_halt_request, + .halt_clear = vfe_halt_clear, + .wm_enable = vfe_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, + .bus_reload_wm = vfe_bus_reload_wm, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .wm_set_subsample = vfe_wm_set_subsample, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .set_xbar_cfg = vfe_set_xbar_cfg, + .set_rdi_cid = vfe_set_rdi_cid, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_common = vfe_enable_irq_common, + .set_demux_cfg = vfe_set_demux_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_qos = vfe_set_qos, + .set_ds = vfe_set_ds, + .set_cgc_override = vfe_set_cgc_override, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_module_cfg = vfe_set_module_cfg, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .isr_read = vfe_isr_read, + .violation_read = vfe_violation_read, + .isr = vfe_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 7f07a22c3e4b..e6f66cfad86d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -691,6 +691,8 @@ static int vfe_enable(struct vfe_line *line) vfe->ops->bus_enable_wr_if(vfe, 1); vfe->ops->set_qos(vfe); + + vfe->ops->set_ds(vfe); } vfe->stream_count++; @@ -1855,6 +1857,8 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, if (camss->version == CAMSS_8x16) vfe->ops = &vfe_ops_4_1; + else if (camss->version == CAMSS_8x96) + vfe->ops = &vfe_ops_4_7; else return -EINVAL; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 19041ba6644a..eaebe833c143 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -118,6 +118,7 @@ struct vfe_hw_ops { void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line); void (*set_clamp_cfg)(struct vfe_device *vfe); void (*set_qos)(struct vfe_device *vfe); + void (*set_ds)(struct vfe_device *vfe); void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable); void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line); void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable); @@ -176,5 +177,6 @@ void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); void msm_vfe_stop_streaming(struct vfe_device *vfe); extern const struct vfe_hw_ops vfe_ops_4_1; +extern const struct vfe_hw_ops vfe_ops_4_7; #endif /* QC_MSM_CAMSS_VFE_H */ -- cgit v1.2.3-70-g09d2 From cba3819d1e93fb77a9323554ccf7b564276c089b Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:35 -0400 Subject: media: camss: Format configuration per hardware version As the 8x16 and 8x96 support different formats, separate the arrays which contain the supported formats. For the VFE also add separate arrays for RDI and PIX subdevices. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 196 +++++++++++++++++++---- drivers/media/platform/qcom/camss/camss-csid.h | 2 + drivers/media/platform/qcom/camss/camss-csiphy.c | 145 ++++++++--------- drivers/media/platform/qcom/camss/camss-csiphy.h | 2 + drivers/media/platform/qcom/camss/camss-ispif.c | 43 ++++- drivers/media/platform/qcom/camss/camss-ispif.h | 2 + drivers/media/platform/qcom/camss/camss-vfe.c | 189 ++++++++++++---------- drivers/media/platform/qcom/camss/camss-vfe.h | 2 + drivers/media/platform/qcom/camss/camss-video.c | 97 ++++++++++- 9 files changed, 467 insertions(+), 211 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 915835ee3271..db960da560c9 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -62,7 +62,7 @@ #define CSID_RESET_TIMEOUT_MS 500 -struct csid_fmts { +struct csid_format { u32 code; u8 data_type; u8 decode_format; @@ -70,7 +70,7 @@ struct csid_fmts { u8 spp; /* bus samples per pixel */ }; -static const struct csid_fmts csid_input_fmts[] = { +static const struct csid_format csid_formats_8x16[] = { { MEDIA_BUS_FMT_UYVY8_2X8, DATA_TYPE_YUV422_8BIT, @@ -185,17 +185,135 @@ static const struct csid_fmts csid_input_fmts[] = { } }; -static const struct csid_fmts *csid_get_fmt_entry(u32 code) +static const struct csid_format csid_formats_8x96[] = { + { + MEDIA_BUS_FMT_UYVY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + } +}; + +static const struct csid_format *csid_get_fmt_entry( + const struct csid_format *formats, + unsigned int nformat, + u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (code == csid_input_fmts[i].code) - return &csid_input_fmts[i]; + for (i = 0; i < nformat; i++) + if (code == formats[i].code) + return &formats[i]; WARN(1, "Unknown format\n"); - return &csid_input_fmts[0]; + return &formats[0]; } /* @@ -242,10 +360,13 @@ static int csid_set_clock_rates(struct csid_device *csid) !strcmp(clock->name, "csi1") || !strcmp(clock->name, "csi2") || !strcmp(clock->name, "csi3")) { - u8 bpp = csid_get_fmt_entry( - csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; + const struct csid_format *f = csid_get_fmt_entry( + csid->formats, + csid->nformats, + csid->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csid->phy.lane_cnt; - u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); + u64 min_rate = pixel_clock * f->bpp / + (2 * num_lanes * 4); long rate; camss_add_clock_margin(&min_rate); @@ -408,9 +529,10 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) /* Config Test Generator */ struct v4l2_mbus_framefmt *f = &csid->fmt[MSM_CSID_PAD_SRC]; - u8 bpp = csid_get_fmt_entry(f->code)->bpp; - u8 spp = csid_get_fmt_entry(f->code)->spp; - u32 num_bytes_per_line = f->width * bpp * spp / 8; + const struct csid_format *format = csid_get_fmt_entry( + csid->formats, csid->nformats, f->code); + u32 num_bytes_per_line = + f->width * format->bpp * format->spp / 8; u32 num_lines = f->height; /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ @@ -426,8 +548,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(ver, 0)); - dt = csid_get_fmt_entry( - csid->fmt[MSM_CSID_PAD_SRC].code)->data_type; + dt = format->data_type; /* 5:0 data type */ val = dt; @@ -439,9 +560,12 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(ver, 0)); - df = csid_get_fmt_entry( - csid->fmt[MSM_CSID_PAD_SRC].code)->decode_format; + df = format->decode_format; } else { + struct v4l2_mbus_framefmt *f = + &csid->fmt[MSM_CSID_PAD_SINK]; + const struct csid_format *format = csid_get_fmt_entry( + csid->formats, csid->nformats, f->code); struct csid_phy_config *phy = &csid->phy; val = phy->lane_cnt - 1; @@ -456,10 +580,8 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); - dt = csid_get_fmt_entry( - csid->fmt[MSM_CSID_PAD_SINK].code)->data_type; - df = csid_get_fmt_entry( - csid->fmt[MSM_CSID_PAD_SINK].code)->decode_format; + dt = format->data_type; + df = format->decode_format; } /* Config LUT */ @@ -534,12 +656,12 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (fmt->code == csid_input_fmts[i].code) + for (i = 0; i < csid->nformats; i++) + if (fmt->code == csid->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) + if (i >= csid->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -563,12 +685,12 @@ static void csid_try_format(struct csid_device *csid, /* Test generator is enabled, set format on source*/ /* pad to allow test generator usage */ - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (csid_input_fmts[i].code == fmt->code) + for (i = 0; i < csid->nformats; i++) + if (csid->formats[i].code == fmt->code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) + if (i >= csid->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -597,10 +719,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) + if (code->index >= csid->nformats) return -EINVAL; - code->code = csid_input_fmts[code->index].code; + code->code = csid->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { if (code->index > 0) @@ -611,10 +733,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, code->code = format->code; } else { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) + if (code->index >= csid->nformats) return -EINVAL; - code->code = csid_input_fmts[code->index].code; + code->code = csid->formats[code->index].code; } } @@ -834,6 +956,18 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->camss = camss; csid->id = id; + if (camss->version == CAMSS_8x16) { + csid->formats = csid_formats_8x16; + csid->nformats = + ARRAY_SIZE(csid_formats_8x16); + } else if (camss->version == CAMSS_8x96) { + csid->formats = csid_formats_8x96; + csid->nformats = + ARRAY_SIZE(csid_formats_8x96); + } else { + return -EINVAL; + } + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index ed605fd3bb87..1824b3745e10 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -58,6 +58,8 @@ struct csid_device { struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *testgen_mode; + const struct csid_format *formats; + unsigned int nformats; }; struct resources; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 7da705147966..3cdab59d24f9 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -23,93 +23,69 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -static const struct { +struct csiphy_format { u32 code; u8 bpp; -} csiphy_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } +}; + +static const struct csiphy_format csiphy_formats_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, +}; + +static const struct csiphy_format csiphy_formats_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, }; /* * csiphy_get_bpp - map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array * @code: media bus format code * * Return number of bits per pixel */ -static u8 csiphy_get_bpp(u32 code) +static u8 csiphy_get_bpp(const struct csiphy_format *formats, + unsigned int nformats, u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (code == csiphy_formats[i].code) - return csiphy_formats[i].bpp; + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].bpp; WARN(1, "Unknown format\n"); - return csiphy_formats[0].bpp; + return formats[0].bpp; } /* @@ -133,7 +109,8 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) if (!strcmp(clock->name, "csiphy0_timer") || !strcmp(clock->name, "csiphy1_timer") || !strcmp(clock->name, "csiphy2_timer")) { - u8 bpp = csiphy_get_bpp( + u8 bpp = csiphy_get_bpp(csiphy->formats, + csiphy->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); @@ -256,7 +233,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) struct csiphy_config *cfg = &csiphy->cfg; u32 pixel_clock; u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); - u8 bpp = csiphy_get_bpp(csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 val; int ret; @@ -361,12 +339,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy, case MSM_CSIPHY_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (fmt->code == csiphy_formats[i].code) + for (i = 0; i < csiphy->nformats; i++) + if (fmt->code == csiphy->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csiphy_formats)) + if (i >= csiphy->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -402,10 +380,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csiphy_formats)) + if (code->index >= csiphy->nformats) return -EINVAL; - code->code = csiphy_formats[code->index].code; + code->code = csiphy->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -563,12 +541,17 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->id = id; csiphy->cfg.combo_mode = 0; - if (camss->version == CAMSS_8x16) + if (camss->version == CAMSS_8x16) { csiphy->ops = &csiphy_ops_2ph_1_0; - else if (camss->version == CAMSS_8x96) + csiphy->formats = csiphy_formats_8x16; + csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); + } else if (camss->version == CAMSS_8x96) { csiphy->ops = &csiphy_ops_3ph_1_0; - else + csiphy->formats = csiphy_formats_8x96; + csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); + } else { return -EINVAL; + } /* Memory */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 5debe46b6cf1..376f865ad383 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -71,6 +71,8 @@ struct csiphy_device { struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; const struct csiphy_hw_ops *ops; + const struct csiphy_format *formats; + unsigned int nformats; }; struct resources; diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index ae8073274624..146d5d26b3c1 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -96,7 +96,26 @@ enum ispif_intf_cmd { CMD_ALL_NO_CHANGE = 0xffffffff, }; -static const u32 ispif_formats[] = { +static const u32 ispif_formats_8x16[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +static const u32 ispif_formats_8x96[] = { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, @@ -780,12 +799,12 @@ static void ispif_try_format(struct ispif_line *line, case MSM_ISPIF_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(ispif_formats); i++) - if (fmt->code == ispif_formats[i]) + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i]) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(ispif_formats)) + if (i >= line->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -823,10 +842,10 @@ static int ispif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_ISPIF_PAD_SINK) { - if (code->index >= ARRAY_SIZE(ispif_formats)) + if (code->index >= line->nformats) return -EINVAL; - code->code = ispif_formats[code->index]; + code->code = line->formats[code->index]; } else { if (code->index > 0) return -EINVAL; @@ -993,6 +1012,18 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, for (i = 0; i < ispif->line_num; i++) { ispif->line[i].ispif = ispif; ispif->line[i].id = i; + + if (to_camss(ispif)->version == CAMSS_8x16) { + ispif->line[i].formats = ispif_formats_8x16; + ispif->line[i].nformats = + ARRAY_SIZE(ispif_formats_8x16); + } else if (to_camss(ispif)->version == CAMSS_8x96) { + ispif->line[i].formats = ispif_formats_8x96; + ispif->line[i].nformats = + ARRAY_SIZE(ispif_formats_8x96); + } else { + return -EINVAL; + } } /* Memory */ diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index 9cd51dcb3ede..1a5ba2425a42 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -43,6 +43,8 @@ struct ispif_line { struct v4l2_subdev subdev; struct media_pad pads[MSM_ISPIF_PADS_NUM]; struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM]; + const u32 *formats; + unsigned int nformats; }; struct ispif_device { diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index e6f66cfad86d..c27097c440f2 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -45,93 +45,83 @@ #define SCALER_RATIO_MAX 16 -static const struct { +struct vfe_format { u32 code; u8 bpp; -} vfe_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } +}; + +static const struct vfe_format formats_rdi_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, +}; + +static const struct vfe_format formats_pix_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, +}; + +static const struct vfe_format formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, +}; + +static const struct vfe_format formats_pix_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, }; /* * vfe_get_bpp - map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array * @code: media bus format code * * Return number of bits per pixel */ -static u8 vfe_get_bpp(u32 code) +static u8 vfe_get_bpp(const struct vfe_format *formats, + unsigned int nformats, u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (code == vfe_formats[i].code) - return vfe_formats[i].bpp; + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].bpp; WARN(1, "Unknown format\n"); - return vfe_formats[0].bpp; + return formats[0].bpp; } /* @@ -978,8 +968,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) if (j == VFE_LINE_PIX) { tmp = pixel_clock[j]; } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); + struct vfe_line *l = &vfe->line[j]; + + bpp = vfe_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -1057,8 +1050,11 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) if (j == VFE_LINE_PIX) { tmp = pixel_clock[j]; } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); + struct vfe_line *l = &vfe->line[j]; + + bpp = vfe_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -1374,12 +1370,12 @@ static void vfe_try_format(struct vfe_line *line, case MSM_VFE_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (fmt->code == vfe_formats[i].code) + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(vfe_formats)) + if (i >= line->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -1539,10 +1535,10 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_VFE_PAD_SINK) { - if (code->index >= ARRAY_SIZE(vfe_formats)) + if (code->index >= line->nformats) return -EINVAL; - code->code = vfe_formats[code->index].code; + code->code = line->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -1943,12 +1939,33 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->reg_update = 0; for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { - vfe->line[i].video_out.type = - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vfe->line[i].video_out.camss = camss; - vfe->line[i].id = i; - init_completion(&vfe->line[i].output.sof); - init_completion(&vfe->line[i].output.reg_update); + struct vfe_line *l = &vfe->line[i]; + + l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + l->video_out.camss = camss; + l->id = i; + init_completion(&l->output.sof); + init_completion(&l->output.reg_update); + + if (camss->version == CAMSS_8x16) { + if (i == VFE_LINE_PIX) { + l->formats = formats_pix_8x16; + l->nformats = ARRAY_SIZE(formats_pix_8x16); + } else { + l->formats = formats_rdi_8x16; + l->nformats = ARRAY_SIZE(formats_rdi_8x16); + } + } else if (camss->version == CAMSS_8x96) { + if (i == VFE_LINE_PIX) { + l->formats = formats_pix_8x96; + l->nformats = ARRAY_SIZE(formats_pix_8x96); + } else { + l->formats = formats_rdi_8x96; + l->nformats = ARRAY_SIZE(formats_rdi_8x96); + } + } else { + return -EINVAL; + } } init_completion(&vfe->reset_complete); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index eaebe833c143..71f6c97b266e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -71,6 +71,8 @@ struct vfe_line { struct v4l2_rect crop; struct camss_video video_out; struct vfe_output output; + const struct vfe_format *formats; + unsigned int nformats; }; struct vfe_device; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 16e74b2448fe..ba7d0c45a8d9 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -41,7 +41,7 @@ struct camss_format_info { unsigned int bpp[3]; }; -static const struct camss_format_info formats_rdi[] = { +static const struct camss_format_info formats_rdi_8x16[] = { { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, { { 1, 1 } }, { { 1, 1 } }, { 16 } }, { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, @@ -76,7 +76,77 @@ static const struct camss_format_info formats_rdi[] = { { { 1, 1 } }, { { 1, 1 } }, { 12 } }, }; -static const struct camss_format_info formats_pix[] = { +static const struct camss_format_info formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, +}; + +static const struct camss_format_info formats_pix_8x16[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, +}; + +static const struct camss_format_info formats_pix_8x96[] = { { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, { { 1, 1 } }, { { 2, 3 } }, { 8 } }, { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, @@ -790,11 +860,24 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, mutex_init(&video->lock); - video->formats = formats_rdi; - video->nformats = ARRAY_SIZE(formats_rdi); - if (is_pix) { - video->formats = formats_pix; - video->nformats = ARRAY_SIZE(formats_pix); + if (video->camss->version == CAMSS_8x16) { + if (is_pix) { + video->formats = formats_pix_8x16; + video->nformats = ARRAY_SIZE(formats_pix_8x16); + } else { + video->formats = formats_rdi_8x16; + video->nformats = ARRAY_SIZE(formats_rdi_8x16); + } + } else if (video->camss->version == CAMSS_8x96) { + if (is_pix) { + video->formats = formats_pix_8x96; + video->nformats = ARRAY_SIZE(formats_pix_8x96); + } else { + video->formats = formats_rdi_8x96; + video->nformats = ARRAY_SIZE(formats_rdi_8x96); + } + } else { + goto error_video_register; } ret = msm_video_init_format(video); -- cgit v1.2.3-70-g09d2 From 07eeb342a1385bbe07d6cb846af05e0add0bbbc2 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:36 -0400 Subject: media: camss: vfe: Different format support on source pad Rework the format selection on the source pad. Make the format on the source pad selectable amongst a list of formats. This list can be different for each sink pad format. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 172 ++++++++++++++++++++------ 1 file changed, 135 insertions(+), 37 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index c27097c440f2..dc353d624e95 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -124,6 +124,131 @@ static u8 vfe_get_bpp(const struct vfe_format *formats, return formats[0].bpp; } +static u32 vfe_find_code(u32 *code, unsigned int n_code, + unsigned int index, u32 req_code) +{ + int i; + + if (!req_code && (index >= n_code)) + return 0; + + for (i = 0; i < n_code; i++) + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + + return code[0]; +} + +static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, + unsigned int index, u32 src_req_code) +{ + struct vfe_device *vfe = to_vfe(line); + + if (vfe->camss->version == CAMSS_8x16) + switch (sink_code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YUYV8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_YVYU8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_YVYU8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_UYVY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_UYVY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_VYUY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_VYUY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + default: + if (index > 0) + return 0; + + return sink_code; + } + else if (vfe->camss->version == CAMSS_8x96) + switch (sink_code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YUYV8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_YVYU8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_YVYU8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_UYVY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_UYVY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_VYUY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_VYUY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + default: + if (index > 0) + return 0; + + return sink_code; + } + else + return 0; +} + /* * vfe_reset - Trigger reset on VFE module and wait to complete * @vfe: VFE device @@ -1388,11 +1513,11 @@ static void vfe_try_format(struct vfe_line *line, case MSM_VFE_PAD_SRC: /* Set and return a format same as sink pad */ - code = fmt->code; - *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - which); + *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); + + fmt->code = vfe_src_pad_code(line, fmt->code, 0, code); if (line->id == VFE_LINE_PIX) { struct v4l2_rect *rect; @@ -1401,34 +1526,6 @@ static void vfe_try_format(struct vfe_line *line, fmt->width = rect->width; fmt->height = rect->height; - - switch (fmt->code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - if (code == MEDIA_BUS_FMT_YUYV8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - if (code == MEDIA_BUS_FMT_YVYU8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YVYU8_2X8; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - if (code == MEDIA_BUS_FMT_UYVY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - if (code == MEDIA_BUS_FMT_VYUY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_VYUY8_2X8; - break; - } } break; @@ -1532,7 +1629,6 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; if (code->pad == MSM_VFE_PAD_SINK) { if (code->index >= line->nformats) @@ -1540,13 +1636,15 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, code->code = line->formats[code->index].code; } else { - if (code->index > 0) - return -EINVAL; + struct v4l2_mbus_framefmt *sink_fmt; - format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - code->which); + sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, + code->which); - code->code = format->code; + code->code = vfe_src_pad_code(line, sink_fmt->code, + code->index, 0); + if (!code->code) + return -EINVAL; } return 0; -- cgit v1.2.3-70-g09d2 From 312e1c858a0f05e27812cd7de87114801fced508 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:37 -0400 Subject: media: camss: vfe: Add support for UYVY output from VFE on 8x96 Add support to output UYVY formats from the VFE (via the PIX interface). A configuration for the realign module in the VFE is added. As the realign module is present on 8x96 but not on 8x16, this is supported on 8x96 only. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe-4-1.c | 6 + drivers/media/platform/qcom/camss/camss-vfe-4-7.c | 129 ++++++++++++++++++---- drivers/media/platform/qcom/camss/camss-vfe.c | 31 +++++- drivers/media/platform/qcom/camss/camss-vfe.h | 2 + drivers/media/platform/qcom/camss/camss-video.c | 8 ++ 5 files changed, 152 insertions(+), 24 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 41184dcc33ca..da3a9fed9f2d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -542,6 +542,11 @@ static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, } } +static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line, + u8 enable) +{ + /* empty */ +} static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) { vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), @@ -989,6 +994,7 @@ const struct vfe_hw_ops vfe_ops_4_1 = { .wm_set_subsample = vfe_wm_set_subsample, .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, .set_xbar_cfg = vfe_set_xbar_cfg, + .set_realign_cfg = vfe_set_realign_cfg, .set_rdi_cid = vfe_set_rdi_cid, .reg_update = vfe_reg_update, .reg_update_clear = vfe_reg_update_clear, diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index 45e6711a346f..4c584bffd179 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -34,6 +34,7 @@ #define VFE_0_MODULE_ZOOM_EN 0x04c #define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1) #define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2) +#define VFE_0_MODULE_ZOOM_EN_REALIGN_BUF BIT(9) #define VFE_0_CORE_CFG 0x050 #define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 @@ -87,6 +88,9 @@ #define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2)) #define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2) +#define VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN BIT(3) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTRA (0x1 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER (0x2 << 4) #define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0 @@ -221,6 +225,11 @@ #define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) #define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) +#define VFE_0_REALIGN_BUF_CFG 0xaac +#define VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL BIT(2) +#define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3) +#define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4) + #define CAMIF_TIMEOUT_SLEEP_US 1000 #define CAMIF_TIMEOUT_ALL_US 1000000 @@ -311,7 +320,7 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) #define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) -static int vfe_word_per_line(u32 format, u32 pixel_per_line) +static int vfe_word_per_line_by_pixel(u32 format, u32 pixel_per_line) { int val = 0; @@ -333,6 +342,11 @@ static int vfe_word_per_line(u32 format, u32 pixel_per_line) return val; } +static int vfe_word_per_line_by_bytes(u32 bytes_per_line) +{ + return CALC_WORD(bytes_per_line, 1, 8); +} + static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, u16 *width, u16 *height, u16 *bytesperline) { @@ -351,6 +365,15 @@ static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, *height = pix->height; *bytesperline = pix->plane_fmt[0].bytesperline; break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[plane].bytesperline; + break; + } } @@ -365,7 +388,7 @@ static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); - wpl = vfe_word_per_line(pix->pixelformat, width); + wpl = vfe_word_per_line_by_pixel(pix->pixelformat, width); reg = height - 1; reg |= ((wpl + 3) / 4 - 1) << 16; @@ -373,7 +396,7 @@ static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - wpl = vfe_word_per_line(pix->pixelformat, bytesperline); + wpl = vfe_word_per_line_by_bytes(bytesperline); reg = 0x3; reg |= (height - 1) << 2; @@ -536,32 +559,97 @@ static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, struct vfe_line *line = container_of(output, struct vfe_line, output); u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; u32 reg; - unsigned int i; - for (i = 0; i < output->wm_num; i++) { - if (i == 0) { - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - } else if (i == 1) { - reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) - reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; - } + switch (p) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + + if (output->wm_idx[0] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + + if (output->wm_idx[1] % 2 == 1) + reg <<= 16; - if (output->wm_idx[i] % 2 == 1) + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]), + reg); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + reg = VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN; + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + + if (p == V4L2_PIX_FMT_YUYV || p == V4L2_PIX_FMT_YVYU) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + + if (output->wm_idx[0] % 2 == 1) reg <<= 16; if (enable) vfe_reg_set(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), reg); else vfe_reg_clr(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), reg); + break; + default: + break; } } +static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line, + u8 enable) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 val = VFE_0_MODULE_ZOOM_EN_REALIGN_BUF; + + if (p != V4L2_PIX_FMT_YUYV && p != V4L2_PIX_FMT_YVYU && + p != V4L2_PIX_FMT_VYUY && p != V4L2_PIX_FMT_UYVY) + return; + + if (enable) { + vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val); + } else { + vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val); + return; + } + + val = VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE; + + if (p == V4L2_PIX_FMT_UYVY || p == V4L2_PIX_FMT_YUYV) + val |= VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL; + else + val |= VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL; + + writel_relaxed(val, vfe->base + VFE_0_REALIGN_BUF_CFG); +} + static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) { vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), @@ -911,11 +999,11 @@ static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) VFE_0_MODULE_ZOOM_EN_CROP_ENC; if (enable) { - writel_relaxed(val_lens, vfe->base + VFE_0_MODULE_LENS_EN); - writel_relaxed(val_zoom, vfe->base + VFE_0_MODULE_ZOOM_EN); + vfe_reg_set(vfe, VFE_0_MODULE_LENS_EN, val_lens); + vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom); } else { - writel_relaxed(0x0, vfe->base + VFE_0_MODULE_LENS_EN); - writel_relaxed(0x0, vfe->base + VFE_0_MODULE_ZOOM_EN); + vfe_reg_clr(vfe, VFE_0_MODULE_LENS_EN, val_lens); + vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom); } } @@ -1028,6 +1116,7 @@ const struct vfe_hw_ops vfe_ops_4_7 = { .wm_set_subsample = vfe_wm_set_subsample, .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, .set_xbar_cfg = vfe_set_xbar_cfg, + .set_realign_cfg = vfe_set_realign_cfg, .set_rdi_cid = vfe_set_rdi_cid, .reg_update = vfe_reg_update, .reg_update_clear = vfe_reg_update_clear, diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index dc353d624e95..d85d663101f5 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -203,6 +203,9 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, { u32 src_code[] = { MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_YUYV8_1_5X8, }; @@ -213,6 +216,9 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, { u32 src_code[] = { MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_YVYU8_1_5X8, }; @@ -223,6 +229,9 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, { u32 src_code[] = { MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_UYVY8_1_5X8, }; @@ -233,6 +242,9 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, { u32 src_code[] = { MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_1_5X8, }; @@ -308,10 +320,6 @@ static void vfe_init_outputs(struct vfe_device *vfe) output->buf[0] = NULL; output->buf[1] = NULL; INIT_LIST_HEAD(&output->pending_bufs); - - output->wm_num = 1; - if (vfe->line[i].id == VFE_LINE_PIX) - output->wm_num = 2; } } @@ -567,6 +575,7 @@ static int vfe_get_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; + struct v4l2_format *f = &line->video_out.active_fmt; unsigned long flags; int i; int wm_idx; @@ -582,6 +591,18 @@ static int vfe_get_output(struct vfe_line *line) output->active_buf = 0; + switch (f->fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + output->wm_num = 2; + break; + default: + output->wm_num = 1; + break; + } + for (i = 0; i < output->wm_num; i++) { wm_idx = vfe_reserve_wm(vfe, line->id); if (wm_idx < 0) { @@ -712,6 +733,7 @@ static int vfe_enable_output(struct vfe_line *line) ops->enable_irq_pix_line(vfe, 0, line->id, 1); ops->set_module_cfg(vfe, 1); ops->set_camif_cfg(vfe, line); + ops->set_realign_cfg(vfe, line, 1); ops->set_xbar_cfg(vfe, output, 1); ops->set_demux_cfg(vfe, line); ops->set_scale_cfg(vfe, line); @@ -776,6 +798,7 @@ static int vfe_disable_output(struct vfe_line *line) ops->enable_irq_pix_line(vfe, 0, line->id, 0); ops->set_module_cfg(vfe, 0); + ops->set_realign_cfg(vfe, line, 0); ops->set_xbar_cfg(vfe, output, 0); ops->set_camif_cmd(vfe, 0); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 71f6c97b266e..0d10071ae881 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -107,6 +107,8 @@ struct vfe_hw_ops { u8 enable); void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id, u8 cid); + void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line, + u8 enable); void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id); void (*reg_update_clear)(struct vfe_device *vfe, enum vfe_line_id line_id); diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index ba7d0c45a8d9..e6e114a62355 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -179,6 +179,14 @@ static const struct camss_format_info formats_pix_8x96[] = { { { 1, 1 } }, { { 1, 2 } }, { 8 } }, { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, }; /* ----------------------------------------------------------------------------- -- cgit v1.2.3-70-g09d2 From 7e37f47f3ae7b73648768d652b669ea23161db5a Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:38 -0400 Subject: media: camss: csid: Different format support on source pad Usually the format on the source pad is the same as on the sink pad. However the CSID is able to do some format conversions. To support this make the format on the source pad selectable amongst a list of formats. This list can be different for each sink pad format. This is still not used but will be when the format conversions are implemented. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 69 +++++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index db960da560c9..cf543fa2c497 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -300,6 +300,47 @@ static const struct csid_format csid_formats_8x96[] = { } }; +static u32 csid_find_code(u32 *code, unsigned int n_code, + unsigned int index, u32 req_code) +{ + int i; + + if (!req_code && (index >= n_code)) + return 0; + + for (i = 0; i < n_code; i++) + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + + return code[0]; +} + +static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, + unsigned int index, u32 src_req_code) +{ + if (csid->camss->version == CAMSS_8x16) { + if (index > 0) + return 0; + + return sink_code; + } else if (csid->camss->version == CAMSS_8x96) { + switch (sink_code) { + default: + if (index > 0) + return 0; + + return sink_code; + } + } else { + return 0; + } +} + static const struct csid_format *csid_get_fmt_entry( const struct csid_format *formats, unsigned int nformat, @@ -674,15 +715,15 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SRC: if (csid->testgen_mode->cur.val == 0) { - /* Test generator is disabled, keep pad formats */ - /* in sync - set and return a format same as sink pad */ - struct v4l2_mbus_framefmt format; + /* Test generator is disabled, */ + /* keep pad formats in sync */ + u32 code = fmt->code; - format = *__csid_get_format(csid, cfg, - MSM_CSID_PAD_SINK, which); - *fmt = format; + *fmt = *__csid_get_format(csid, cfg, + MSM_CSID_PAD_SINK, which); + fmt->code = csid_src_pad_code(csid, fmt->code, 0, code); } else { - /* Test generator is enabled, set format on source*/ + /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ for (i = 0; i < csid->nformats; i++) @@ -716,7 +757,6 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSID_PAD_SINK) { if (code->index >= csid->nformats) @@ -725,13 +765,16 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, code->code = csid->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { - if (code->index > 0) - return -EINVAL; + struct v4l2_mbus_framefmt *sink_fmt; - format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, - code->which); + sink_fmt = __csid_get_format(csid, cfg, + MSM_CSID_PAD_SINK, + code->which); - code->code = format->code; + code->code = csid_src_pad_code(csid, sink_fmt->code, + code->index, 0); + if (!code->code) + return -EINVAL; } else { if (code->index >= csid->nformats) return -EINVAL; -- cgit v1.2.3-70-g09d2 From 5019d7c8209c539226f9e64c38eece7fce732e18 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:39 -0400 Subject: media: camss: csid: MIPI10 to Plain16 format conversion Use the PRDI mode on 8x96 to allow to configure RAW MIPI10 to Plain16 format conversion. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 33 ++++++++++++- drivers/media/platform/qcom/camss/camss-ispif.c | 64 +++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-vfe.c | 1 + drivers/media/platform/qcom/camss/camss-video.c | 2 + 4 files changed, 99 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index cf543fa2c497..0715a8e326b1 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -32,6 +32,15 @@ (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n)) #define CAMSS_CSID_CID_n_CFG(v, n) \ (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0) +#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1) +#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4 +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (0 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (1 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9) +#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9) +#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10) +#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10) #define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064) #define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068) #define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c) @@ -330,6 +339,16 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, return sink_code; } else if (csid->camss->version == CAMSS_8x96) { switch (sink_code) { + case MEDIA_BUS_FMT_SBGGR10_1X10: + { + u32 src_code[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + }; + + return csid_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } default: if (index > 0) return 0; @@ -636,7 +655,19 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(ver, vc)); - val = (df << 4) | 0x3; + val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; + val |= CAMSS_CSID_CID_n_CFG_RDI_EN; + val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; + if (csid->camss->version == CAMSS_8x96 && + csid->fmt[MSM_CSID_PAD_SINK].code == + MEDIA_BUS_FMT_SBGGR10_1X10 && + csid->fmt[MSM_CSID_PAD_SRC].code == + MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; + } writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(ver, cid)); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 146d5d26b3c1..81d6351c54c6 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -76,6 +76,13 @@ (0x254 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \ (0x264 + 0x200 * (m) + 0x4 * (n)) +/* PACK_CFG registers are 8x96 only */ +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(m, n) \ + (0x270 + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(m, n) \ + (0x27c + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(c) \ + (1 << ((cid % 8) * 4)) #define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ (0x2c0 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ @@ -128,6 +135,7 @@ static const u32 ispif_formats_8x96[] = { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, @@ -666,6 +674,54 @@ static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf, writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); } +/* + * ispif_config_pack - Config packing for PRDI mode + * @ispif: ISPIF device + * @code: media bus format code + * @intf: VFE interface + * @cid: desired CID to handle + * @vfe: VFE HW module id + * @enable: enable or disable + */ +static void ispif_config_pack(struct ispif_device *ispif, u32 code, + enum ispif_intf intf, u8 cid, u8 vfe, u8 enable) +{ + u32 addr, val; + + if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) + return; + + switch (intf) { + case RDI0: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 0); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 0); + break; + case RDI1: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 1); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 1); + break; + case RDI2: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 2); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 2); + break; + default: + return; + } + + if (enable) + val = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(cid); + else + val = 0; + + writel_relaxed(val, ispif->base + addr); +} + /* * ispif_set_intf_cmd - Set command to enable/disable interface * @ispif: ISPIF device @@ -734,6 +790,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) ispif_select_csid(ispif, intf, csid, vfe, 1); ispif_select_cid(ispif, intf, cid, vfe, 1); ispif_config_irq(ispif, intf, vfe, 1); + if (to_camss(ispif)->version == CAMSS_8x96) + ispif_config_pack(ispif, + line->fmt[MSM_ISPIF_PAD_SINK].code, + intf, cid, vfe, 1); ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY, intf, vfe, vc); } else { @@ -747,6 +807,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) return ret; mutex_lock(&ispif->config_lock); + if (to_camss(ispif)->version == CAMSS_8x96) + ispif_config_pack(ispif, + line->fmt[MSM_ISPIF_PAD_SINK].code, + intf, cid, vfe, 0); ispif_config_irq(ispif, intf, vfe, 0); ispif_select_cid(ispif, intf, cid, vfe, 0); ispif_select_csid(ispif, intf, csid, vfe, 0); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index d85d663101f5..11d37b78c3cd 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -89,6 +89,7 @@ static const struct vfe_format formats_rdi_8x96[] = { { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index e6e114a62355..28d53bf4c784 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -101,6 +101,8 @@ static const struct camss_format_info formats_rdi_8x96[] = { { { 1, 1 } }, { { 1, 1 } }, { 10 } }, { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, { { 1, 1 } }, { { 1, 1 } }, { 12 } }, { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, -- cgit v1.2.3-70-g09d2 From f476fb568f98bb6b2d2499ee7059504bf5203a91 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:40 -0400 Subject: media: camss: Add support for RAW MIPI14 on 8x96 Add support for RAW MIPI14 format for RDI mode on 8x96. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 30 ++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-csiphy.c | 4 ++++ drivers/media/platform/qcom/camss/camss-ispif.c | 4 ++++ drivers/media/platform/qcom/camss/camss-vfe.c | 4 ++++ drivers/media/platform/qcom/camss/camss-video.c | 8 +++++++ 5 files changed, 50 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 0715a8e326b1..472884d1546e 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -63,11 +63,13 @@ #define DATA_TYPE_RAW_8BIT 0x2a #define DATA_TYPE_RAW_10BIT 0x2b #define DATA_TYPE_RAW_12BIT 0x2c +#define DATA_TYPE_RAW_14BIT 0x2d #define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 #define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 #define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 #define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 +#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8 #define CSID_RESET_TIMEOUT_MS 500 @@ -306,6 +308,34 @@ static const struct csid_format csid_formats_8x96[] = { DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, } }; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 3cdab59d24f9..cae3e8b9ab37 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -64,6 +64,10 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, }; /* diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 81d6351c54c6..3e2f34148977 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -140,6 +140,10 @@ static const u32 ispif_formats_8x96[] = { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, }; /* diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 11d37b78c3cd..96753090e072 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -94,6 +94,10 @@ static const struct vfe_format formats_rdi_8x96[] = { { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, }; static const struct vfe_format formats_pix_8x96[] = { diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 28d53bf4c784..2e19bc8c4161 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -111,6 +111,14 @@ static const struct camss_format_info formats_rdi_8x96[] = { { { 1, 1 } }, { { 1, 1 } }, { 12 } }, { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, }; static const struct camss_format_info formats_pix_8x16[] = { -- cgit v1.2.3-70-g09d2 From cc8fe07398e390d2047c30849f3055d5074ee833 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:41 -0400 Subject: media: camss: Add support for 10-bit grayscale formats Add support for 10-bit packed V4L2_PIX_FMT_Y10P (on 8x16 and 8x96) and unpacked V4L2_PIX_FMT_Y10 (on 8x96 only) pixel formats. Signed-off-by: Todor Tomov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 50 +++++++++++++++++++----- drivers/media/platform/qcom/camss/camss-csiphy.c | 2 + drivers/media/platform/qcom/camss/camss-ispif.c | 6 ++- drivers/media/platform/qcom/camss/camss-vfe.c | 3 ++ drivers/media/platform/qcom/camss/camss-video.c | 6 +++ 5 files changed, 56 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 472884d1546e..6e141af6b6df 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -193,7 +193,14 @@ static const struct csid_format csid_formats_8x16[] = { DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, - } + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, }; static const struct csid_format csid_formats_8x96[] = { @@ -336,7 +343,14 @@ static const struct csid_format csid_formats_8x96[] = { DECODE_FORMAT_UNCOMPRESSED_14_BIT, 14, 1, - } + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, }; static u32 csid_find_code(u32 *code, unsigned int n_code, @@ -379,6 +393,16 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, return csid_find_code(src_code, ARRAY_SIZE(src_code), index, src_req_code); } + case MEDIA_BUS_FMT_Y10_1X10: + { + u32 src_code[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, + }; + + return csid_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } default: if (index > 0) return 0; @@ -689,15 +713,21 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) val |= CAMSS_CSID_CID_n_CFG_RDI_EN; val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; - if (csid->camss->version == CAMSS_8x96 && - csid->fmt[MSM_CSID_PAD_SINK].code == - MEDIA_BUS_FMT_SBGGR10_1X10 && - csid->fmt[MSM_CSID_PAD_SRC].code == - MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { - val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; - val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; - val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; + + if (csid->camss->version == CAMSS_8x96) { + u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; + u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; + + if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 && + src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) || + (sink_code == MEDIA_BUS_FMT_Y10_1X10 && + src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) { + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; + } } + writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(ver, cid)); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index cae3e8b9ab37..4559f3b1b38c 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -45,6 +45,7 @@ static const struct csiphy_format csiphy_formats_8x16[] = { { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; static const struct csiphy_format csiphy_formats_8x96[] = { @@ -68,6 +69,7 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; /* diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 3e2f34148977..7f269021d08c 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -120,6 +120,7 @@ static const u32 ispif_formats_8x16[] = { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_Y10_1X10, }; static const u32 ispif_formats_8x96[] = { @@ -144,6 +145,8 @@ static const u32 ispif_formats_8x96[] = { MEDIA_BUS_FMT_SGBRG14_1X14, MEDIA_BUS_FMT_SGRBG14_1X14, MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, }; /* @@ -692,7 +695,8 @@ static void ispif_config_pack(struct ispif_device *ispif, u32 code, { u32 addr, val; - if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) + if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE && + code != MEDIA_BUS_FMT_Y10_2X8_PADHI_LE) return; switch (intf) { diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 96753090e072..ed6a557de65d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -67,6 +67,7 @@ static const struct vfe_format formats_rdi_8x16[] = { { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; static const struct vfe_format formats_pix_8x16[] = { @@ -98,6 +99,8 @@ static const struct vfe_format formats_rdi_8x96[] = { { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, }; static const struct vfe_format formats_pix_8x96[] = { diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 2e19bc8c4161..c9bb0d023db4 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -74,6 +74,8 @@ static const struct camss_format_info formats_rdi_8x16[] = { { { 1, 1 } }, { { 1, 1 } }, { 12 } }, { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, }; static const struct camss_format_info formats_rdi_8x96[] = { @@ -119,6 +121,10 @@ static const struct camss_format_info formats_rdi_8x96[] = { { { 1, 1 } }, { { 1, 1 } }, { 14 } }, { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, }; static const struct camss_format_info formats_pix_8x16[] = { -- cgit v1.2.3-70-g09d2 From 988b3ae3e0e9374b6be3b4e58524cfa6f224d8d5 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Wed, 25 Jul 2018 12:38:43 -0400 Subject: media: camss: csid: Add support for events triggered by user controls Changing a user control value can trigger an event to other users. Add support for that. Signed-off-by: Todor Tomov Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 6e141af6b6df..729b31891466 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "camss-csid.h" @@ -1273,6 +1274,8 @@ static int csid_link_setup(struct media_entity *entity, static const struct v4l2_subdev_core_ops csid_core_ops = { .s_power = csid_set_power, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops csid_video_ops = { @@ -1318,7 +1321,8 @@ int msm_csid_register_entity(struct csid_device *csid, v4l2_subdev_init(sd, &csid_v4l2_ops); sd->internal_ops = &csid_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", MSM_CSID_NAME, csid->id); v4l2_set_subdevdata(sd, csid); -- cgit v1.2.3-70-g09d2 From aa53bf0b03ae5e276c2714dc68e375d032bc9110 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 10 Jul 2018 12:18:29 -0400 Subject: media: cx231xx: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-audio.c | 10 ++++++---- drivers/media/usb/cx231xx/cx231xx-core.c | 10 ++++++---- drivers/media/usb/cx231xx/cx231xx-vbi.c | 5 +++-- 3 files changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index c4a84fb930b6..0025187c28e3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -126,6 +126,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) stride = runtime->frame_bits >> 3; for (i = 0; i < urb->number_of_packets; i++) { + unsigned long flags; int length = urb->iso_frame_desc[i].actual_length / stride; cp = (unsigned char *)urb->transfer_buffer + @@ -148,7 +149,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) length * stride); } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); dev->adev.hwptr_done_capture += length; if (dev->adev.hwptr_done_capture >= @@ -163,7 +164,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) runtime->period_size; period_elapsed = 1; } - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); } if (period_elapsed) snd_pcm_period_elapsed(substream); @@ -216,6 +217,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) stride = runtime->frame_bits >> 3; if (1) { + unsigned long flags; int length = urb->actual_length / stride; cp = (unsigned char *)urb->transfer_buffer; @@ -234,7 +236,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) length * stride); } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); dev->adev.hwptr_done_capture += length; if (dev->adev.hwptr_done_capture >= @@ -249,7 +251,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) runtime->period_size; period_elapsed = 1; } - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); } if (period_elapsed) snd_pcm_period_elapsed(substream); diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 53d846dea3d2..493c2dca6244 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -799,6 +799,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + unsigned long flags; int i; switch (urb->status) { @@ -815,9 +816,9 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) } /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); + spin_lock_irqsave(&dev->video_mode.slock, flags); dev->video_mode.isoc_ctl.isoc_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { @@ -844,6 +845,7 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + unsigned long flags; switch (urb->status) { case 0: /* success */ @@ -862,9 +864,9 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) } /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); + spin_lock_irqsave(&dev->video_mode.slock, flags); dev->video_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); /* Reset urb buffers */ urb->status = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index b621cf1aa96b..920417baf893 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -305,6 +305,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + unsigned long flags; switch (urb->status) { case 0: /* success */ @@ -321,9 +322,9 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) } /* Copy data from URB */ - spin_lock(&dev->vbi_mode.slock); + spin_lock_irqsave(&dev->vbi_mode.slock, flags); dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->vbi_mode.slock); + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); /* Reset status */ urb->status = 0; -- cgit v1.2.3-70-g09d2 From a4733b5248af10d5088ba06495e3faad9f7822b3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 10 Jul 2018 12:18:31 -0400 Subject: media: go7007: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Hans Verkuil Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/go7007/go7007-driver.c | 9 +++++---- drivers/media/usb/go7007/snd-go7007.c | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index 05b1126f263e..62aeebcdd7f7 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -448,13 +448,14 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf { u32 *bytesused; struct go7007_buffer *vb_tmp = NULL; + unsigned long flags; if (vb == NULL) { - spin_lock(&go->spinlock); + spin_lock_irqsave(&go->spinlock, flags); if (!list_empty(&go->vidq_active)) vb = go->active_buf = list_first_entry(&go->vidq_active, struct go7007_buffer, list); - spin_unlock(&go->spinlock); + spin_unlock_irqrestore(&go->spinlock, flags); go->next_seq++; return vb; } @@ -468,7 +469,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf vb->vb.vb2_buf.timestamp = ktime_get_ns(); vb_tmp = vb; - spin_lock(&go->spinlock); + spin_lock_irqsave(&go->spinlock, flags); list_del(&vb->list); if (list_empty(&go->vidq_active)) vb = NULL; @@ -476,7 +477,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); go->active_buf = vb; - spin_unlock(&go->spinlock); + spin_unlock_irqrestore(&go->spinlock, flags); vb2_buffer_done(&vb_tmp->vb.vb2_buf, VB2_BUF_STATE_DONE); return vb; } diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index f84a2130f033..137fc253b122 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -75,13 +75,14 @@ static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) struct go7007_snd *gosnd = go->snd_context; struct snd_pcm_runtime *runtime = gosnd->substream->runtime; int frames = bytes_to_frames(runtime, length); + unsigned long flags; - spin_lock(&gosnd->lock); + spin_lock_irqsave(&gosnd->lock, flags); gosnd->hw_ptr += frames; if (gosnd->hw_ptr >= runtime->buffer_size) gosnd->hw_ptr -= runtime->buffer_size; gosnd->avail += frames; - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); if (gosnd->w_idx + length > runtime->dma_bytes) { int cpy = runtime->dma_bytes - gosnd->w_idx; @@ -92,13 +93,13 @@ static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) } memcpy(runtime->dma_area + gosnd->w_idx, buf, length); gosnd->w_idx += length; - spin_lock(&gosnd->lock); + spin_lock_irqsave(&gosnd->lock, flags); if (gosnd->avail < runtime->period_size) { - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); return; } gosnd->avail -= runtime->period_size; - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); if (gosnd->capturing) snd_pcm_period_elapsed(gosnd->substream); } -- cgit v1.2.3-70-g09d2 From 320905baa1dbddd3991c287432176d536e1d5b79 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 10 Jul 2018 12:18:33 -0400 Subject: media: usbtv: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Hans Verkuil Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbtv/usbtv-audio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c index 2c2ca77fa01f..4ce38246ed64 100644 --- a/drivers/media/usb/usbtv/usbtv-audio.c +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -126,6 +126,7 @@ static void usbtv_audio_urb_received(struct urb *urb) struct snd_pcm_runtime *runtime = substream->runtime; size_t i, frame_bytes, chunk_length, buffer_pos, period_pos; int period_elapsed; + unsigned long flags; void *urb_current; switch (urb->status) { @@ -179,12 +180,12 @@ static void usbtv_audio_urb_received(struct urb *urb) } } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); chip->snd_buffer_pos = buffer_pos; chip->snd_period_pos = period_pos; - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); if (period_elapsed) snd_pcm_period_elapsed(substream); -- cgit v1.2.3-70-g09d2 From 6706fe55af6fc2bcb069a77620ae38662bf09d1c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 Jul 2018 05:48:35 -0400 Subject: media: dvb_ca_en50221: off by one in dvb_ca_en50221_io_do_ioctl() The > should be >= so we don't read one element beyond the end of the ca->slot_info[] array. The array is allocated in dvb_ca_en50221_init(). Signed-off-by: Dan Carpenter Acked-by: Jasmin Jessich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 1310526b0d49..4d371cea0d5d 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1391,7 +1391,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, struct dvb_ca_slot *sl; slot = info->num; - if ((slot > ca->slot_count) || (slot < 0)) { + if ((slot >= ca->slot_count) || (slot < 0)) { err = -EINVAL; goto out_unlock; } -- cgit v1.2.3-70-g09d2 From a3f90c75b833caeff123499e13e0e31cbecf7d5b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2018 18:59:35 -0400 Subject: media: dvb: convert tuner_info frequencies to Hz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now, satellite tuner drivers specify frequencies in kHz, while terrestrial/cable ones specify in Hz. That's confusing for developers. However, the main problem is that universal tuners capable of handling both satellite and non-satelite delivery systems are appearing. We end by needing to hack the drivers in order to support such hybrid tuners. So, convert everything to specify tuner frequencies in Hz. Plese notice that a similar patch is also needed for frontends. Tested-by: Katsuhiro Suzuki Acked-by: Michael Büsch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 25 +++++++++++++++++++++---- drivers/media/dvb-frontends/ascot2e.c | 6 +++--- drivers/media/dvb-frontends/cx24113.c | 8 ++++---- drivers/media/dvb-frontends/dib0070.c | 8 ++++---- drivers/media/dvb-frontends/dib0090.c | 12 ++++++------ drivers/media/dvb-frontends/dvb-pll.c | 16 ++++++++++++++-- drivers/media/dvb-frontends/helene.c | 12 ++++++------ drivers/media/dvb-frontends/horus3a.c | 6 +++--- drivers/media/dvb-frontends/itd1000.c | 8 ++++---- drivers/media/dvb-frontends/ix2505v.c | 4 ++-- drivers/media/dvb-frontends/stb6000.c | 4 ++-- drivers/media/dvb-frontends/stb6100.c | 5 ++--- drivers/media/dvb-frontends/stv6110.c | 6 +++--- drivers/media/dvb-frontends/stv6110x.c | 7 +++---- drivers/media/dvb-frontends/stv6111.c | 5 ++--- drivers/media/dvb-frontends/tda18271c2dd.c | 6 +++--- drivers/media/dvb-frontends/tda665x.c | 6 +++--- drivers/media/dvb-frontends/tda8261.c | 9 ++++----- drivers/media/dvb-frontends/tda826x.c | 4 ++-- drivers/media/dvb-frontends/ts2020.c | 4 ++-- drivers/media/dvb-frontends/tua6100.c | 6 +++--- drivers/media/dvb-frontends/zl10036.c | 4 ++-- drivers/media/tuners/e4000.c | 6 +++--- drivers/media/tuners/fc0011.c | 6 +++--- drivers/media/tuners/fc0012.c | 7 +++---- drivers/media/tuners/fc0013.c | 7 +++---- drivers/media/tuners/fc2580.c | 6 +++--- drivers/media/tuners/it913x.c | 6 +++--- drivers/media/tuners/m88rs6000t.c | 6 +++--- drivers/media/tuners/max2165.c | 8 ++++---- drivers/media/tuners/mc44s803.c | 8 ++++---- drivers/media/tuners/mt2060.c | 8 ++++---- drivers/media/tuners/mt2063.c | 7 +++---- drivers/media/tuners/mt2131.c | 8 ++++---- drivers/media/tuners/mt2266.c | 8 ++++---- drivers/media/tuners/mxl301rf.c | 4 ++-- drivers/media/tuners/mxl5005s.c | 8 ++++---- drivers/media/tuners/qm1d1b0004.c | 4 ++-- drivers/media/tuners/qm1d1c0042.c | 4 ++-- drivers/media/tuners/qt1010.c | 8 ++++---- drivers/media/tuners/qt1010_priv.h | 14 ++++++++------ drivers/media/tuners/r820t.c | 6 +++--- drivers/media/tuners/si2157.c | 6 +++--- drivers/media/tuners/tda18212.c | 8 ++++---- drivers/media/tuners/tda18218.c | 8 ++++---- drivers/media/tuners/tda18250.c | 6 +++--- drivers/media/tuners/tda18271-fe.c | 6 +++--- drivers/media/tuners/tda827x.c | 12 ++++++------ drivers/media/tuners/tua9001.c | 6 +++--- drivers/media/tuners/tuner-xc2028.c | 6 +++--- drivers/media/tuners/xc4000.c | 12 ++++++------ drivers/media/tuners/xc5000.c | 12 ++++++------ drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c | 6 +++--- include/media/dvb_frontend.h | 19 ++++++++++--------- 54 files changed, 221 insertions(+), 196 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index ce25aef39008..75e95b56f8b3 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -896,14 +896,31 @@ static int dvb_frontend_start(struct dvb_frontend *fe) static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, u32 *freq_min, u32 *freq_max) { - *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + __u32 tuner_min = fe->ops.tuner_ops.info.frequency_min_hz; + __u32 tuner_max = fe->ops.tuner_ops.info.frequency_max_hz; + + /* If the standard is for satellite, convert frequencies to kHz */ + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + tuner_max /= kHz; + tuner_min /= kHz; + break; + default: + break; + } + + *freq_min = max(fe->ops.info.frequency_min, tuner_min); if (fe->ops.info.frequency_max == 0) - *freq_max = fe->ops.tuner_ops.info.frequency_max; - else if (fe->ops.tuner_ops.info.frequency_max == 0) + *freq_max = tuner_max; + else if (tuner_max == 0) *freq_max = fe->ops.info.frequency_max; else - *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max); + *freq_max = min(fe->ops.info.frequency_max, tuner_max); if (*freq_min == 0 || *freq_max == 0) dev_warn(fe->dvb->device, diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index 9746c6dd7fb8..52ce0e6e2a15 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -468,9 +468,9 @@ static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops ascot2e_tuner_ops = { .info = { .name = "Sony ASCOT2E", - .frequency_min = 1000000, - .frequency_max = 1200000000, - .frequency_step = 25000, + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, }, .init = ascot2e_init, .release = ascot2e_release, diff --git a/drivers/media/dvb-frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c index 037db3e9d2dd..91a5033b6bd7 100644 --- a/drivers/media/dvb-frontends/cx24113.c +++ b/drivers/media/dvb-frontends/cx24113.c @@ -533,10 +533,10 @@ static void cx24113_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops cx24113_tuner_ops = { .info = { - .name = "Conexant CX24113", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 125, + .name = "Conexant CX24113", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, }, .release = cx24113_release, diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c index 932d235118e2..37ebd5af8fd4 100644 --- a/drivers/media/dvb-frontends/dib0070.c +++ b/drivers/media/dvb-frontends/dib0070.c @@ -726,10 +726,10 @@ static void dib0070_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops dib0070_ops = { .info = { - .name = "DiBcom DiB0070", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .name = "DiBcom DiB0070", + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0070_release, diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index ee7af34979ed..44a074261e69 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -2578,9 +2578,9 @@ static int dib0090_set_params(struct dvb_frontend *fe) static const struct dvb_tuner_ops dib0090_ops = { .info = { .name = "DiBcom DiB0090", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0090_release, @@ -2593,9 +2593,9 @@ static const struct dvb_tuner_ops dib0090_ops = { static const struct dvb_tuner_ops dib0090_fw_ops = { .info = { .name = "DiBcom DiB0090", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0090_release, diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index 4a663420190e..6d4b2eec67b4 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -799,6 +799,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct dvb_pll_priv *priv = NULL; int ret; const struct dvb_pll_desc *desc; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; b1 = kmalloc(1, GFP_KERNEL); if (!b1) @@ -844,8 +845,19 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, strncpy(fe->ops.tuner_ops.info.name, desc->name, sizeof(fe->ops.tuner_ops.info.name)); - fe->ops.tuner_ops.info.frequency_min = desc->min; - fe->ops.tuner_ops.info.frequency_max = desc->max; + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + fe->ops.tuner_ops.info.frequency_min_hz = desc->min * kHz; + fe->ops.tuner_ops.info.frequency_max_hz = desc->max * kHz; + break; + default: + fe->ops.tuner_ops.info.frequency_min_hz = desc->min; + fe->ops.tuner_ops.info.frequency_max_hz = desc->max; + } + if (!desc->initdata) fe->ops.tuner_ops.init = NULL; if (!desc->sleepdata) diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index a5de65dcf784..dc70fb684681 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -846,9 +846,9 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops helene_tuner_ops = { .info = { .name = "Sony HELENE Ter tuner", - .frequency_min = 1000000, - .frequency_max = 1200000000, - .frequency_step = 25000, + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, }, .init = helene_init, .release = helene_release, @@ -860,9 +860,9 @@ static const struct dvb_tuner_ops helene_tuner_ops = { static const struct dvb_tuner_ops helene_tuner_ops_s = { .info = { .name = "Sony HELENE Sat tuner", - .frequency_min = 500000, - .frequency_max = 2500000, - .frequency_step = 1000, + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = helene_init, .release = helene_release, diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 5e7e265a52e6..02bc08081971 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -330,9 +330,9 @@ static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops horus3a_tuner_ops = { .info = { .name = "Sony Horus3a", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = horus3a_init, .release = horus3a_release, diff --git a/drivers/media/dvb-frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c index 04f7f6854f73..c3a6e81ae87f 100644 --- a/drivers/media/dvb-frontends/itd1000.c +++ b/drivers/media/dvb-frontends/itd1000.c @@ -353,10 +353,10 @@ static void itd1000_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops itd1000_tuner_ops = { .info = { - .name = "Integrant ITD1000", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 125, /* kHz for QPSK frontends */ + .name = "Integrant ITD1000", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, }, .release = itd1000_release, diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index 965012ad5c59..9c055f72c416 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -256,8 +256,8 @@ static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops ix2505v_tuner_ops = { .info = { .name = "Sharp IX2505V (B0017)", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .release = ix2505v_release, .set_params = ix2505v_set_params, diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c index 69c03892f2da..786b9eccde00 100644 --- a/drivers/media/dvb-frontends/stb6000.c +++ b/drivers/media/dvb-frontends/stb6000.c @@ -188,8 +188,8 @@ static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops stb6000_tuner_ops = { .info = { .name = "ST STB6000", - .frequency_min = 950000, - .frequency_max = 2150000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz }, .release = stb6000_release, .sleep = stb6000_sleep, diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c index 3a851f524b16..30ac584dfab3 100644 --- a/drivers/media/dvb-frontends/stb6100.c +++ b/drivers/media/dvb-frontends/stb6100.c @@ -527,9 +527,8 @@ static int stb6100_set_params(struct dvb_frontend *fe) static const struct dvb_tuner_ops stb6100_ops = { .info = { .name = "STB6100 Silicon Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = stb6100_init, diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c index 6aad0efa3174..7db9a5bceccc 100644 --- a/drivers/media/dvb-frontends/stv6110.c +++ b/drivers/media/dvb-frontends/stv6110.c @@ -371,9 +371,9 @@ static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops stv6110_tuner_ops = { .info = { .name = "ST STV6110", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = stv6110_init, .release = stv6110_release, diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index d8950028d021..82c002d3833a 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -347,10 +347,9 @@ static void stv6110x_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops stv6110x_ops = { .info = { - .name = "STV6110(A) Silicon Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0, + .name = "STV6110(A) Silicon Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .release = stv6110x_release }; diff --git a/drivers/media/dvb-frontends/stv6111.c b/drivers/media/dvb-frontends/stv6111.c index 9b715b6fe152..0cf460111acb 100644 --- a/drivers/media/dvb-frontends/stv6111.c +++ b/drivers/media/dvb-frontends/stv6111.c @@ -646,9 +646,8 @@ static int get_rf_strength(struct dvb_frontend *fe, u16 *st) static const struct dvb_tuner_ops tuner_ops = { .info = { .name = "ST STV6111", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .set_params = set_params, .release = release, diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index fcffc7b4acf7..5ce58612315d 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -1215,9 +1215,9 @@ static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops tuner_ops = { .info = { .name = "NXP TDA18271C2D", - .frequency_min = 47125000, - .frequency_max = 865000000, - .frequency_step = 62500 + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 865 * MHz, + .frequency_step_hz = 62500 }, .init = init, .sleep = sleep, diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c index 3ef7140ed7f3..8766c9ff6680 100644 --- a/drivers/media/dvb-frontends/tda665x.c +++ b/drivers/media/dvb-frontends/tda665x.c @@ -231,9 +231,9 @@ struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, info = &fe->ops.tuner_ops.info; memcpy(info->name, config->name, sizeof(config->name)); - info->frequency_min = config->frequency_min; - info->frequency_max = config->frequency_max; - info->frequency_step = config->frequency_offst; + info->frequency_min_hz = config->frequency_min; + info->frequency_max_hz = config->frequency_max; + info->frequency_step_hz = config->frequency_offst; printk(KERN_DEBUG "%s: Attaching TDA665x (%s) tuner\n", __func__, info->name); diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c index f72a54e7eb23..500f50b81b66 100644 --- a/drivers/media/dvb-frontends/tda8261.c +++ b/drivers/media/dvb-frontends/tda8261.c @@ -163,10 +163,9 @@ static void tda8261_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda8261_ops = { .info = { - .name = "TDA8261", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0 + .name = "TDA8261", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .set_params = tda8261_set_params, @@ -190,7 +189,7 @@ struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, fe->tuner_priv = state; fe->ops.tuner_ops = tda8261_ops; - fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; + fe->ops.tuner_ops.info.frequency_step_hz = div_tab[config->step_size] * kHz; pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c index da427b4c2aaa..100da5d5fdc5 100644 --- a/drivers/media/dvb-frontends/tda826x.c +++ b/drivers/media/dvb-frontends/tda826x.c @@ -131,8 +131,8 @@ static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tda826x_tuner_ops = { .info = { .name = "Philips TDA826X", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .release = tda826x_release, .sleep = tda826x_sleep, diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index c55882a8da06..3e3e40878633 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -498,8 +498,8 @@ static int ts2020_read_signal_strength(struct dvb_frontend *fe, static const struct dvb_tuner_ops ts2020_tuner_ops = { .info = { .name = "TS2020", - .frequency_min = 950000, - .frequency_max = 2150000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz }, .init = ts2020_init, .release = ts2020_release, diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c index 1d41abd47f04..b233b7be0b84 100644 --- a/drivers/media/dvb-frontends/tua6100.c +++ b/drivers/media/dvb-frontends/tua6100.c @@ -155,9 +155,9 @@ static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tua6100_tuner_ops = { .info = { .name = "Infineon TUA6100", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .release = tua6100_release, .sleep = tua6100_sleep, diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c index 89dd65ae88ad..e5a432fd84c3 100644 --- a/drivers/media/dvb-frontends/zl10036.c +++ b/drivers/media/dvb-frontends/zl10036.c @@ -443,8 +443,8 @@ static int zl10036_init(struct dvb_frontend *fe) static const struct dvb_tuner_ops zl10036_tuner_ops = { .info = { .name = "Zarlink ZL10036", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .init = zl10036_init, .release = zl10036_release, diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index b5b9d87ba75c..fbec1a13dc6a 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -610,9 +610,9 @@ static int e4000_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops e4000_dvb_tuner_ops = { .info = { - .name = "Elonics E4000", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "Elonics E4000", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = e4000_dvb_init, diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 145407dee3db..a983899c6b0b 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -472,10 +472,10 @@ static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops fc0011_tuner_ops = { .info = { - .name = "Fitipower FC0011", + .name = "Fitipower FC0011", - .frequency_min = 45000000, - .frequency_max = 1000000000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 1000 * MHz, }, .release = fc0011_release, diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 625ac6f51c39..e992b98ae5bc 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -415,11 +415,10 @@ exit: static const struct dvb_tuner_ops fc0012_tuner_ops = { .info = { - .name = "Fitipower FC0012", + .name = "Fitipower FC0012", - .frequency_min = 37000000, /* estimate */ - .frequency_max = 862000000, /* estimate */ - .frequency_step = 0, + .frequency_min_hz = 37 * MHz, /* estimate */ + .frequency_max_hz = 862 * MHz, /* estimate */ }, .release = fc0012_release, diff --git a/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c index e606118d1a9b..fc62afb1450d 100644 --- a/drivers/media/tuners/fc0013.c +++ b/drivers/media/tuners/fc0013.c @@ -574,11 +574,10 @@ exit: static const struct dvb_tuner_ops fc0013_tuner_ops = { .info = { - .name = "Fitipower FC0013", + .name = "Fitipower FC0013", - .frequency_min = 37000000, /* estimate */ - .frequency_max = 1680000000, /* CHECK */ - .frequency_step = 0, + .frequency_min_hz = 37 * MHz, /* estimate */ + .frequency_max_hz = 1680 * MHz, /* CHECK */ }, .release = fc0013_release, diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 743184ae0d26..db26892aac84 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -355,9 +355,9 @@ static int fc2580_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops fc2580_dvb_tuner_ops = { .info = { - .name = "FCI FC2580", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "FCI FC2580", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = fc2580_dvb_init, diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c index 27e5bc1c3cb5..b5eb39921e95 100644 --- a/drivers/media/tuners/it913x.c +++ b/drivers/media/tuners/it913x.c @@ -375,9 +375,9 @@ err: static const struct dvb_tuner_ops it913x_tuner_ops = { .info = { - .name = "ITE IT913X", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "ITE IT913X", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = it913x_init, diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 9f3e0fd4cad9..3df2f23a40be 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -569,9 +569,9 @@ err: static const struct dvb_tuner_ops m88rs6000t_tuner_ops = { .info = { - .name = "Montage M88RS6000 Internal Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, + .name = "Montage M88RS6000 Internal Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = m88rs6000t_init, diff --git a/drivers/media/tuners/max2165.c b/drivers/media/tuners/max2165.c index 20ceb72e530b..721d8f722efb 100644 --- a/drivers/media/tuners/max2165.c +++ b/drivers/media/tuners/max2165.c @@ -377,10 +377,10 @@ static void max2165_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops max2165_tuner_ops = { .info = { - .name = "Maxim MAX2165", - .frequency_min = 470000000, - .frequency_max = 862000000, - .frequency_step = 50000, + .name = "Maxim MAX2165", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = max2165_release, diff --git a/drivers/media/tuners/mc44s803.c b/drivers/media/tuners/mc44s803.c index 403c6b2aa53b..2023e081d9ad 100644 --- a/drivers/media/tuners/mc44s803.c +++ b/drivers/media/tuners/mc44s803.c @@ -300,10 +300,10 @@ static int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops mc44s803_tuner_ops = { .info = { - .name = "Freescale MC44S803", - .frequency_min = 48000000, - .frequency_max = 1000000000, - .frequency_step = 100000, + .name = "Freescale MC44S803", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 1000 * MHz, + .frequency_step_hz = 100 * kHz, }, .release = mc44s803_release, diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 3d3c6815b6a7..4ace77cfe285 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -395,10 +395,10 @@ static void mt2060_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2060_tuner_ops = { .info = { - .name = "Microtune MT2060", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "Microtune MT2060", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2060_release, diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 80dc3e241b4a..f4c8a7293ebb 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -2200,10 +2200,9 @@ static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) static const struct dvb_tuner_ops mt2063_ops = { .info = { .name = "MT2063 Silicon Tuner", - .frequency_min = 45000000, - .frequency_max = 865000000, - .frequency_step = 0, - }, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 865 * MHz, + }, .init = mt2063_init, .sleep = MT2063_Sleep, diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c index 659bf19dc434..086a7b7cf634 100644 --- a/drivers/media/tuners/mt2131.c +++ b/drivers/media/tuners/mt2131.c @@ -235,10 +235,10 @@ static void mt2131_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2131_tuner_ops = { .info = { - .name = "Microtune MT2131", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "Microtune MT2131", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2131_release, diff --git a/drivers/media/tuners/mt2266.c b/drivers/media/tuners/mt2266.c index f4545b7f5da2..e6cc78720de4 100644 --- a/drivers/media/tuners/mt2266.c +++ b/drivers/media/tuners/mt2266.c @@ -304,10 +304,10 @@ static void mt2266_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2266_tuner_ops = { .info = { - .name = "Microtune MT2266", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_step = 50000, + .name = "Microtune MT2266", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2266_release, .init = mt2266_init, diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index 57b0e4862aaf..c628435a1b06 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -271,8 +271,8 @@ static const struct dvb_tuner_ops mxl301rf_ops = { .info = { .name = "MaxLinear MxL301RF", - .frequency_min = 93000000, - .frequency_max = 803142857, + .frequency_min_hz = 93 * MHz, + .frequency_max_hz = 803 * MHz + 142857, }, .init = mxl301rf_init, diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index 355ef2959b7d..ec584316c812 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -4075,10 +4075,10 @@ static void mxl5005s_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mxl5005s_tuner_ops = { .info = { - .name = "MaxLinear MXL5005S", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "MaxLinear MXL5005S", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mxl5005s_release, diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index b4495cc1626b..008ad870c00f 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -186,8 +186,8 @@ static const struct dvb_tuner_ops qm1d1b0004_ops = { .info = { .name = "Sharp qm1d1b0004", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = qm1d1b0004_init, diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 642a065b9a07..83ca5dc047ea 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -388,8 +388,8 @@ static const struct dvb_tuner_ops qm1d1c0042_ops = { .info = { .name = "Sharp QM1D1C0042", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = qm1d1c0042_init, diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c index b92be882ab3c..4565c06b1617 100644 --- a/drivers/media/tuners/qt1010.c +++ b/drivers/media/tuners/qt1010.c @@ -394,10 +394,10 @@ static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops qt1010_tuner_ops = { .info = { - .name = "Quantek QT1010", - .frequency_min = QT1010_MIN_FREQ, - .frequency_max = QT1010_MAX_FREQ, - .frequency_step = QT1010_STEP, + .name = "Quantek QT1010", + .frequency_min_hz = QT1010_MIN_FREQ, + .frequency_max_hz = QT1010_MAX_FREQ, + .frequency_step_hz = QT1010_STEP, }, .release = qt1010_release, diff --git a/drivers/media/tuners/qt1010_priv.h b/drivers/media/tuners/qt1010_priv.h index 4cb78ecc8985..f25324c63067 100644 --- a/drivers/media/tuners/qt1010_priv.h +++ b/drivers/media/tuners/qt1010_priv.h @@ -71,12 +71,14 @@ reg def meaning 2f 00 ? not used? */ -#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers, - hw could be more precise but we don't - know how to use */ -#define QT1010_MIN_FREQ 48000000 /* 48 MHz */ -#define QT1010_MAX_FREQ 860000000 /* 860 MHz */ -#define QT1010_OFFSET 1246000000 /* 1246 MHz */ +#define QT1010_STEP (125 * kHz) /* + * used by Windows drivers, + * hw could be more precise but we don't + * know how to use + */ +#define QT1010_MIN_FREQ (48 * MHz) +#define QT1010_MAX_FREQ (860 * MHz) +#define QT1010_OFFSET (1246 * MHz) #define QT1010_WR 0 #define QT1010_RD 1 diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 3e14b9e2e763..ba4be08a8551 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2297,9 +2297,9 @@ static void r820t_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops r820t_tuner_ops = { .info = { - .name = "Rafael Micro R820T", - .frequency_min = 42000000, - .frequency_max = 1002000000, + .name = "Rafael Micro R820T", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 1002 * MHz, }, .init = r820t_init, .release = r820t_release, diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 9e34d31d724d..a08d8fe2bb1b 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -387,9 +387,9 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", - .frequency_min = 42000000, - .frequency_max = 870000000, + .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 870 * MHz, }, .init = si2157_init, diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 7b8068354fea..8326106ec2e3 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -175,11 +175,11 @@ static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tda18212_tuner_ops = { .info = { - .name = "NXP TDA18212", + .name = "NXP TDA18212", - .frequency_min = 48000000, - .frequency_max = 864000000, - .frequency_step = 1000, + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 1 * kHz, }, .set_params = tda18212_set_params, diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index c56fcf5d48e3..cbbd4d5e15da 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -269,11 +269,11 @@ static void tda18218_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda18218_tuner_ops = { .info = { - .name = "NXP TDA18218", + .name = "NXP TDA18218", - .frequency_min = 174000000, - .frequency_max = 864000000, - .frequency_step = 1000, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = tda18218_release, diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index 20d12b063380..20d10ef45ab6 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -740,9 +740,9 @@ static int tda18250_sleep(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda18250_ops = { .info = { - .name = "NXP TDA18250", - .frequency_min = 42000000, - .frequency_max = 870000000, + .name = "NXP TDA18250", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 870 * MHz, }, .init = tda18250_init, diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 147155553648..4d69029229e4 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1240,9 +1240,9 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) static const struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", - .frequency_min = 45000000, - .frequency_max = 864000000, - .frequency_step = 62500 + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 62500 }, .init = tda18271_init, .sleep = tda18271_sleep, diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index 8400808f8f7f..4391dabba510 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -816,9 +816,9 @@ static int tda827x_initial_sleep(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda827xo_tuner_ops = { .info = { .name = "Philips TDA827X", - .frequency_min = 55000000, - .frequency_max = 860000000, - .frequency_step = 250000 + .frequency_min_hz = 55 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 250 * kHz }, .release = tda827x_release, .init = tda827x_initial_init, @@ -832,9 +832,9 @@ static const struct dvb_tuner_ops tda827xo_tuner_ops = { static const struct dvb_tuner_ops tda827xa_tuner_ops = { .info = { .name = "Philips TDA827XA", - .frequency_min = 44000000, - .frequency_max = 906000000, - .frequency_step = 62500 + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 906 * MHz, + .frequency_step_hz = 62500 }, .release = tda827x_release, .init = tda827x_init, diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index 9d70378fe2d3..5c89a130b47d 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -164,9 +164,9 @@ static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tua9001_tuner_ops = { .info = { - .name = "Infineon TUA9001", - .frequency_min = 170000000, - .frequency_max = 862000000, + .name = "Infineon TUA9001", + .frequency_min_hz = 170 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = tua9001_init, diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 84744e138982..222b93ef31c0 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1440,9 +1440,9 @@ unlock: static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .info = { .name = "Xceive XC3028", - .frequency_min = 42000000, - .frequency_max = 864000000, - .frequency_step = 50000, + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 50 * kHz, }, .set_config = xc2028_set_config, diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index f0fa8da08afa..76b3f37f24a8 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -398,8 +398,8 @@ static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz) dprintk(1, "%s(%u)\n", __func__, freq_hz); - if ((freq_hz > xc4000_tuner_ops.info.frequency_max) || - (freq_hz < xc4000_tuner_ops.info.frequency_min)) + if ((freq_hz > xc4000_tuner_ops.info.frequency_max_hz) || + (freq_hz < xc4000_tuner_ops.info.frequency_min_hz)) return -EINVAL; freq_code = (u16)(freq_hz / 15625); @@ -1635,10 +1635,10 @@ static void xc4000_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops xc4000_tuner_ops = { .info = { - .name = "Xceive XC4000", - .frequency_min = 1000000, - .frequency_max = 1023000000, - .frequency_step = 50000, + .name = "Xceive XC4000", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1023 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = xc4000_release, diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index f7a8d05d1758..f6b65278e502 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -460,8 +460,8 @@ static int xc_set_rf_frequency(struct xc5000_priv *priv, u32 freq_hz) dprintk(1, "%s(%u)\n", __func__, freq_hz); - if ((freq_hz > xc5000_tuner_ops.info.frequency_max) || - (freq_hz < xc5000_tuner_ops.info.frequency_min)) + if ((freq_hz > xc5000_tuner_ops.info.frequency_max_hz) || + (freq_hz < xc5000_tuner_ops.info.frequency_min_hz)) return -EINVAL; freq_code = (u16)(freq_hz / 15625); @@ -1350,10 +1350,10 @@ static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg) static const struct dvb_tuner_ops xc5000_tuner_ops = { .info = { - .name = "Xceive XC5000", - .frequency_min = 1000000, - .frequency_max = 1023000000, - .frequency_step = 50000, + .name = "Xceive XC5000", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1023 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = xc5000_release, diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index 240d736bf1bb..92b3b9221a21 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -465,9 +465,9 @@ static const struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { .info = { .name = "MaxLinear MxL111SF", #if 0 - .frequency_min = , - .frequency_max = , - .frequency_step = , + .frequency_min_hz = , + .frequency_max_hz = , + .frequency_step_hz = , #endif }, #if 0 diff --git a/include/media/dvb_frontend.h b/include/media/dvb_frontend.h index 331c8269c00e..aebaec2dc725 100644 --- a/include/media/dvb_frontend.h +++ b/include/media/dvb_frontend.h @@ -52,6 +52,10 @@ */ #define MAX_DELSYS 8 +/* Helper definitions to be used at frontend drivers */ +#define kHz 1000UL +#define MHz 1000000UL + /** * struct dvb_frontend_tune_settings - parameters to adjust frontend tuning * @@ -73,22 +77,19 @@ struct dvb_frontend; * struct dvb_tuner_info - Frontend name and min/max ranges/bandwidths * * @name: name of the Frontend - * @frequency_min: minimal frequency supported - * @frequency_max: maximum frequency supported - * @frequency_step: frequency step + * @frequency_min_hz: minimal frequency supported in Hz + * @frequency_max_hz: maximum frequency supported in Hz + * @frequency_step_hz: frequency step in Hz * @bandwidth_min: minimal frontend bandwidth supported * @bandwidth_max: maximum frontend bandwidth supported * @bandwidth_step: frontend bandwidth step - * - * NOTE: frequency parameters are in Hz, for terrestrial/cable or kHz for - * satellite. */ struct dvb_tuner_info { char name[128]; - u32 frequency_min; - u32 frequency_max; - u32 frequency_step; + u32 frequency_min_hz; + u32 frequency_max_hz; + u32 frequency_step_hz; u32 bandwidth_min; u32 bandwidth_max; -- cgit v1.2.3-70-g09d2 From f1b1eabff0eb3fc46b06668de8174c0f23b271fd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2018 18:59:36 -0400 Subject: media: dvb: represent min/max/step/tolerance freqs in Hz Right now, satellite frontend drivers specify frequencies in kHz, while terrestrial/cable ones specify in Hz. That's confusing for developers. However, the main problem is that universal frontends capable of handling both satellite and non-satelite delivery systems are appearing. We end by needing to hack the drivers in order to support such hybrid frontends. So, convert everything to specify frontend frequencies in Hz. Tested-by: Katsuhiro Suzuki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/siano/smsdvb-main.c | 6 +- drivers/media/dvb-core/dvb_frontend.c | 84 ++++++++++++++++------- drivers/media/dvb-frontends/af9013.c | 7 +- drivers/media/dvb-frontends/af9033.c | 7 +- drivers/media/dvb-frontends/as102_fe.c | 6 +- drivers/media/dvb-frontends/atbm8830.c | 6 +- drivers/media/dvb-frontends/au8522_dig.c | 6 +- drivers/media/dvb-frontends/bcm3510.c | 6 +- drivers/media/dvb-frontends/cx22700.c | 6 +- drivers/media/dvb-frontends/cx22702.c | 6 +- drivers/media/dvb-frontends/cx24110.c | 8 +-- drivers/media/dvb-frontends/cx24116.c | 8 +-- drivers/media/dvb-frontends/cx24117.c | 8 +-- drivers/media/dvb-frontends/cx24120.c | 8 +-- drivers/media/dvb-frontends/cx24123.c | 8 +-- drivers/media/dvb-frontends/cxd2820r_t.c | 4 +- drivers/media/dvb-frontends/cxd2820r_t2.c | 4 +- drivers/media/dvb-frontends/cxd2841er.c | 9 ++- drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 6 +- drivers/media/dvb-frontends/dib3000mb.c | 6 +- drivers/media/dvb-frontends/dib3000mc.c | 6 +- drivers/media/dvb-frontends/dib7000m.c | 6 +- drivers/media/dvb-frontends/dib7000p.c | 6 +- drivers/media/dvb-frontends/dib8000.c | 6 +- drivers/media/dvb-frontends/dib9000.c | 6 +- drivers/media/dvb-frontends/drx39xyj/drxj.c | 6 +- drivers/media/dvb-frontends/drxd_hard.c | 7 +- drivers/media/dvb-frontends/drxk_hard.c | 8 +-- drivers/media/dvb-frontends/ds3000.c | 8 +-- drivers/media/dvb-frontends/dvb_dummy_fe.c | 24 +++---- drivers/media/dvb-frontends/gp8psk-fe.c | 6 +- drivers/media/dvb-frontends/ix2505v.c | 4 +- drivers/media/dvb-frontends/l64781.c | 7 +- drivers/media/dvb-frontends/lg2160.c | 12 ++-- drivers/media/dvb-frontends/lgdt3305.c | 12 ++-- drivers/media/dvb-frontends/lgdt3306a.c | 6 +- drivers/media/dvb-frontends/lgdt330x.c | 12 ++-- drivers/media/dvb-frontends/lgs8gl5.c | 7 +- drivers/media/dvb-frontends/lgs8gxx.c | 6 +- drivers/media/dvb-frontends/m88ds3103.c | 6 +- drivers/media/dvb-frontends/m88rs2000.c | 8 +-- drivers/media/dvb-frontends/mb86a16.c | 7 +- drivers/media/dvb-frontends/mb86a20s.c | 6 +- drivers/media/dvb-frontends/mt312.c | 10 +-- drivers/media/dvb-frontends/mt352.c | 7 +- drivers/media/dvb-frontends/mxl5xx.c | 6 +- drivers/media/dvb-frontends/nxt200x.c | 6 +- drivers/media/dvb-frontends/nxt6000.c | 6 +- drivers/media/dvb-frontends/or51132.c | 6 +- drivers/media/dvb-frontends/or51211.c | 8 +-- drivers/media/dvb-frontends/rtl2830.c | 4 +- drivers/media/dvb-frontends/rtl2832.c | 10 +-- drivers/media/dvb-frontends/s5h1409.c | 6 +- drivers/media/dvb-frontends/s5h1411.c | 6 +- drivers/media/dvb-frontends/s5h1420.c | 8 +-- drivers/media/dvb-frontends/s5h1432.c | 6 +- drivers/media/dvb-frontends/s921.c | 7 +- drivers/media/dvb-frontends/si2165.c | 2 +- drivers/media/dvb-frontends/si21xx.c | 7 +- drivers/media/dvb-frontends/sp8870.c | 6 +- drivers/media/dvb-frontends/sp887x.c | 6 +- drivers/media/dvb-frontends/stb0899_drv.c | 6 +- drivers/media/dvb-frontends/stv0288.c | 7 +- drivers/media/dvb-frontends/stv0297.c | 6 +- drivers/media/dvb-frontends/stv0299.c | 7 +- drivers/media/dvb-frontends/stv0367.c | 20 +++--- drivers/media/dvb-frontends/stv0900_core.c | 7 +- drivers/media/dvb-frontends/stv090x.c | 6 +- drivers/media/dvb-frontends/stv0910.c | 6 +- drivers/media/dvb-frontends/tc90522.c | 10 +-- drivers/media/dvb-frontends/tda10021.c | 10 +-- drivers/media/dvb-frontends/tda10023.c | 6 +- drivers/media/dvb-frontends/tda10048.c | 6 +- drivers/media/dvb-frontends/tda1004x.c | 12 ++-- drivers/media/dvb-frontends/tda10071.c | 10 +-- drivers/media/dvb-frontends/tda10086.c | 6 +- drivers/media/dvb-frontends/tda8083.c | 7 +- drivers/media/dvb-frontends/ves1820.c | 6 +- drivers/media/dvb-frontends/ves1x93.c | 8 +-- drivers/media/dvb-frontends/zl10036.c | 4 +- drivers/media/dvb-frontends/zl10353.c | 7 +- drivers/media/firewire/firedtv-fe.c | 26 +++---- drivers/media/pci/bt8xx/dst.c | 26 +++---- drivers/media/pci/bt8xx/dvb-bt8xx.c | 8 +-- drivers/media/pci/ddbridge/ddbridge-sx8.c | 6 +- drivers/media/pci/mantis/mantis_vp3030.c | 4 +- drivers/media/tuners/mxl5007t.c | 2 - drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c | 6 +- drivers/media/usb/dvb-usb/af9005-fe.c | 6 +- drivers/media/usb/dvb-usb/cinergyT2-fe.c | 6 +- drivers/media/usb/dvb-usb/dtt200u-fe.c | 6 +- drivers/media/usb/dvb-usb/friio-fe.c | 11 ++- drivers/media/usb/dvb-usb/vp702x-fe.c | 7 +- drivers/media/usb/dvb-usb/vp7045-fe.c | 6 +- drivers/media/usb/ttusb-dec/ttusbdecfe.c | 12 ++-- include/media/dvb_frontend.h | 30 +++++++- 96 files changed, 428 insertions(+), 399 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index c0faad1ba428..43cfd1dbda01 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -1047,9 +1047,9 @@ static void smsdvb_release(struct dvb_frontend *fe) static const struct dvb_frontend_ops smsdvb_fe_ops = { .info = { .name = "Siano Mobile Digital MDTV Receiver", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 75e95b56f8b3..fe0fae482ba4 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -894,11 +894,28 @@ static int dvb_frontend_start(struct dvb_frontend *fe) } static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, - u32 *freq_min, u32 *freq_max) + u32 *freq_min, u32 *freq_max, + u32 *tolerance) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - __u32 tuner_min = fe->ops.tuner_ops.info.frequency_min_hz; - __u32 tuner_max = fe->ops.tuner_ops.info.frequency_max_hz; + u32 tuner_min = fe->ops.tuner_ops.info.frequency_min_hz; + u32 tuner_max = fe->ops.tuner_ops.info.frequency_max_hz; + u32 frontend_min = fe->ops.info.frequency_min_hz; + u32 frontend_max = fe->ops.info.frequency_max_hz; + + *freq_min = max(frontend_min, tuner_min); + + if (frontend_max == 0) + *freq_max = tuner_max; + else if (tuner_max == 0) + *freq_max = frontend_max; + else + *freq_max = min(frontend_max, tuner_max); + + if (*freq_min == 0 || *freq_max == 0) + dev_warn(fe->dvb->device, + "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", + fe->dvb->num, fe->id); /* If the standard is for satellite, convert frequencies to kHz */ switch (c->delivery_system) { @@ -906,26 +923,35 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, case SYS_DVBS2: case SYS_TURBO: case SYS_ISDBS: - tuner_max /= kHz; - tuner_min /= kHz; + *freq_min /= kHz; + *freq_max /= kHz; + if (tolerance) + *tolerance = fe->ops.info.frequency_tolerance_hz / kHz; + break; default: + if (tolerance) + *tolerance = fe->ops.info.frequency_tolerance_hz; break; } +} - *freq_min = max(fe->ops.info.frequency_min, tuner_min); - - if (fe->ops.info.frequency_max == 0) - *freq_max = tuner_max; - else if (tuner_max == 0) - *freq_max = fe->ops.info.frequency_max; - else - *freq_max = min(fe->ops.info.frequency_max, tuner_max); +static u32 dvb_frontend_get_stepsize(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 step = fe->ops.info.frequency_stepsize_hz; + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + step /= kHz; + break; + default: + break; + } - if (*freq_min == 0 || *freq_max == 0) - dev_warn(fe->dvb->device, - "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", - fe->dvb->num, fe->id); + return step; } static int dvb_frontend_check_parameters(struct dvb_frontend *fe) @@ -935,7 +961,7 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) u32 freq_max; /* range check: frequency */ - dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max); + dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max, NULL); if ((freq_min && c->frequency < freq_min) || (freq_max && c->frequency > freq_max)) { dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", @@ -2261,8 +2287,8 @@ static int dtv_set_frontend(struct dvb_frontend *fe) case SYS_ISDBT: case SYS_DTMB: fepriv->min_delay = HZ / 20; - fepriv->step_size = fe->ops.info.frequency_stepsize * 2; - fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + fepriv->step_size = dvb_frontend_get_stepsize(fe) * 2; + fepriv->max_drift = (dvb_frontend_get_stepsize(fe) * 2) + 1; break; default: /* @@ -2391,9 +2417,17 @@ static int dvb_frontend_handle_ioctl(struct file *file, case FE_GET_INFO: { struct dvb_frontend_info *info = parg; - - memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); - dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); + memset(info, 0, sizeof(*info)); + + strcpy(info->name, fe->ops.info.name); + info->symbol_rate_min = fe->ops.info.symbol_rate_min; + info->symbol_rate_max = fe->ops.info.symbol_rate_max; + info->symbol_rate_tolerance = fe->ops.info.symbol_rate_tolerance; + info->caps = fe->ops.info.caps; + info->frequency_stepsize = dvb_frontend_get_stepsize(fe); + dvb_frontend_get_frequency_limits(fe, &info->frequency_min, + &info->frequency_max, + &info->frequency_tolerance); /* * Associate the 4 delivery systems supported by DVBv3 @@ -2423,10 +2457,10 @@ static int dvb_frontend_handle_ioctl(struct file *file, dev_err(fe->dvb->device, "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", __func__, c->delivery_system); - fe->ops.info.type = FE_OFDM; + info->type = FE_OFDM; } dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n", - __func__, c->delivery_system, fe->ops.info.type); + __func__, c->delivery_system, info->type); /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */ if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 482bce49819a..f3acbb57d48c 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1136,10 +1136,9 @@ static const struct dvb_frontend_ops af9013_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Afatech AF9013", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 250000, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index aaed7cfe5f66..0cd57013ea25 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1020,10 +1020,9 @@ static const struct dvb_frontend_ops af9033_ops = { .delsys = {SYS_DVBT}, .info = { .name = "Afatech AF9033 (DVB-T)", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 250000, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c index 9b2f2da1d989..f59a102b0a64 100644 --- a/drivers/media/dvb-frontends/as102_fe.c +++ b/drivers/media/dvb-frontends/as102_fe.c @@ -419,9 +419,9 @@ static const struct dvb_frontend_ops as102_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Abilis AS102 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c index 7b0f3239dbba..cbcc65dc9d54 100644 --- a/drivers/media/dvb-frontends/atbm8830.c +++ b/drivers/media/dvb-frontends/atbm8830.c @@ -428,9 +428,9 @@ static const struct dvb_frontend_ops atbm8830_ops = { .delsys = { SYS_DTMB }, .info = { .name = "AltoBeam ATBM8830/8831 DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c index 8f659bd1c79e..076f737aa8c0 100644 --- a/drivers/media/dvb-frontends/au8522_dig.c +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -897,9 +897,9 @@ static const struct dvb_frontend_ops au8522_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Auvitek AU8522 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 05df133dc5be..e92542b92d34 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -840,10 +840,8 @@ static const struct dvb_frontend_ops bcm3510_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Broadcom BCM3510 VSB/QAM frontend", - .frequency_min = 54000000, - .frequency_max = 803000000, - /* stepsize is just a guess */ - .frequency_stepsize = 0, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 803 * MHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c index 9ffd2c7ac74a..961380162cdd 100644 --- a/drivers/media/dvb-frontends/cx22700.c +++ b/drivers/media/dvb-frontends/cx22700.c @@ -412,9 +412,9 @@ static const struct dvb_frontend_ops cx22700_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Conexant CX22700 DVB-T", - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c index e8b1e6b7e7e5..ab9b2924bcca 100644 --- a/drivers/media/dvb-frontends/cx22702.c +++ b/drivers/media/dvb-frontends/cx22702.c @@ -622,9 +622,9 @@ static const struct dvb_frontend_ops cx22702_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Conexant CX22702 DVB-T", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c index 2f3a1c237489..9441bdc73097 100644 --- a/drivers/media/dvb-frontends/cx24110.c +++ b/drivers/media/dvb-frontends/cx24110.c @@ -629,10 +629,10 @@ static const struct dvb_frontend_ops cx24110_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Conexant CX24110 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index 2dbc7349d870..220f26663647 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -1465,10 +1465,10 @@ static const struct dvb_frontend_ops cx24116_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24116/CX24118", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index ba55d75d916c..667bc8be848d 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -1622,10 +1622,10 @@ static const struct dvb_frontend_ops cx24117_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24117/CX24132", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index ccbabdae6a69..dd3ec316e7c2 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -1555,10 +1555,10 @@ static const struct dvb_frontend_ops cx24120_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24120/CX24118", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index bf33e7390aaf..e49215020a93 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -1111,10 +1111,10 @@ static const struct dvb_frontend_ops cx24123_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Conexant CX24123/CX24109", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index c2e7caf9b010..eb1d7478fa8d 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -431,8 +431,8 @@ int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index e641fde75379..f330ec1710b4 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -426,8 +426,8 @@ int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 1500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 85905d3503ff..c98093ed3dd7 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3942,9 +3942,8 @@ static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Sony CXD2841ER DVB-S/S2 demodulator", - .frequency_min = 500000, - .frequency_max = 2500000, - .frequency_stepsize = 0, + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, @@ -3988,8 +3987,8 @@ static struct dvb_frontend_ops cxd2841er_t_c_ops = { FE_CAN_HIERARCHY_AUTO | FE_CAN_MUTE_TS | FE_CAN_2G_MODULATION, - .frequency_min = 42000000, - .frequency_max = 1002000000, + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 1002 * MHz, .symbol_rate_min = 870000, .symbol_rate_max = 11700000 }, diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c index bd9101e246d5..f87e27481ea7 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -1833,9 +1833,9 @@ static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe) static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = { .info = { .name = "Sony CXD2880", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 1000, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 1 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c index 5861f346db49..bbbd53280477 100644 --- a/drivers/media/dvb-frontends/dib3000mb.c +++ b/drivers/media/dvb-frontends/dib3000mb.c @@ -786,9 +786,9 @@ static const struct dvb_frontend_ops dib3000mb_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 3000M-B DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c index 7e5d474806a5..c9e1db251723 100644 --- a/drivers/media/dvb-frontends/dib3000mc.c +++ b/drivers/media/dvb-frontends/dib3000mc.c @@ -944,9 +944,9 @@ static const struct dvb_frontend_ops dib3000mc_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 3000MC/P", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c index 6a1d357d0c7c..b79358d09de6 100644 --- a/drivers/media/dvb-frontends/dib7000m.c +++ b/drivers/media/dvb-frontends/dib7000m.c @@ -1443,9 +1443,9 @@ static const struct dvb_frontend_ops dib7000m_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 7000MA/MB/PA/PB/MC", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 5a8dbc0b25fb..58387860b62d 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2824,9 +2824,9 @@ static const struct dvb_frontend_ops dib7000p_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 7000PC", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 22eec8f65485..3c3f8cb14845 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4390,9 +4390,9 @@ static const struct dvb_frontend_ops dib8000_ops = { .delsys = { SYS_ISDBT }, .info = { .name = "DiBcom 8000 ISDB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index b8edb55696bb..0183fb1346ef 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -2553,9 +2553,9 @@ static const struct dvb_frontend_ops dib9000_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 9000", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 5706898e84cc..2ddb7d218ace 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12374,9 +12374,9 @@ static const struct dvb_frontend_ops drx39xxj_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Micronas DRX39xxj family Frontend", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 3b7d31a22d82..11fc259e4383 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2912,10 +2912,9 @@ static const struct dvb_frontend_ops drxd_ops = { .delsys = { SYS_DVBT}, .info = { .name = "Micronas DRXD DVB-T", - .frequency_min = 47125000, - .frequency_max = 855250000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 855250 * kHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 5a26ad93be10..ac10781d3550 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -6744,13 +6744,13 @@ static const struct dvb_frontend_ops drxk_ops = { /* .delsys will be filled dynamically */ .info = { .name = "DRXK", - .frequency_min = 47000000, - .frequency_max = 865000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, /* For DVB-C */ - .symbol_rate_min = 870000, + .symbol_rate_min = 870000, .symbol_rate_max = 11700000, /* For DVB-T */ - .frequency_stepsize = 166667, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 2ff90e5eabce..46a55146cb07 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -1100,10 +1100,10 @@ static const struct dvb_frontend_ops ds3000_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Montage Technology DS3000", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c index 6650d4f61ef2..a4cbcae7967d 100644 --- a/drivers/media/dvb-frontends/dvb_dummy_fe.c +++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c @@ -170,9 +170,9 @@ static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Dummy DVB-T", - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | @@ -201,11 +201,11 @@ static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "Dummy DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = (57840000 / 2) / 64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (57840000 / 2) / 4, /* SACLK/4 */ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO @@ -230,10 +230,10 @@ static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Dummy DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 250, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 250 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c index a772ef8bfe1c..238f09aa72f2 100644 --- a/drivers/media/dvb-frontends/gp8psk-fe.c +++ b/drivers/media/dvb-frontends/gp8psk-fe.c @@ -355,9 +355,9 @@ static const struct dvb_frontend_ops gp8psk_fe_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Genpix DVB-S", - .frequency_min = 800000, - .frequency_max = 2250000, - .frequency_stepsize = 100, + .frequency_min_hz = 800 * MHz, + .frequency_max_hz = 2250 * MHz, + .frequency_stepsize_hz = 100 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index 9c055f72c416..a30707b61b1f 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -135,8 +135,8 @@ static int ix2505v_set_params(struct dvb_frontend *fe) u8 gain, cc, ref, psc, local_osc, lpf; u8 data[4] = {0}; - if ((frequency < fe->ops.info.frequency_min) - || (frequency > fe->ops.info.frequency_max)) + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; if (state->config->tuner_gain) diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c index 249c18761e6e..9afb5bf6424b 100644 --- a/drivers/media/dvb-frontends/l64781.c +++ b/drivers/media/dvb-frontends/l64781.c @@ -575,10 +575,9 @@ static const struct dvb_frontend_ops l64781_ops = { .delsys = { SYS_DVBT }, .info = { .name = "LSI L64781 DVB-T", - /* .frequency_min = ???,*/ - /* .frequency_max = ???,*/ - .frequency_stepsize = 166666, - /* .frequency_tolerance = ???,*/ + /* .frequency_min_hz = ???,*/ + /* .frequency_max_hz = ???,*/ + .frequency_stepsize_hz = 166666, /* .symbol_rate_tolerance = ???,*/ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index 9854096839ae..408151e33fa7 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1349,9 +1349,9 @@ static const struct dvb_frontend_ops lg2160_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2160 ATSC/MH Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, }, .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, #if 0 @@ -1375,9 +1375,9 @@ static const struct dvb_frontend_ops lg2161_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2161 ATSC/MH Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, }, .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, #if 0 diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index 735d73060265..857e9b4d69b4 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -1164,9 +1164,9 @@ static const struct dvb_frontend_ops lgdt3304_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3304 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, @@ -1187,9 +1187,9 @@ static const struct dvb_frontend_ops lgdt3305_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3305 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 32de824476db..0e1f5daaf20c 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2157,9 +2157,9 @@ static const struct dvb_frontend_ops lgdt3306a_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3306A VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl, diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index f6731738b073..10d584ce538d 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -944,9 +944,9 @@ static const struct dvb_frontend_ops lgdt3302_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3302 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB @@ -966,9 +966,9 @@ static const struct dvb_frontend_ops lgdt3303_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3303 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB diff --git a/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c index f47e5a1af16d..07e5bcee9c1e 100644 --- a/drivers/media/dvb-frontends/lgs8gl5.c +++ b/drivers/media/dvb-frontends/lgs8gl5.c @@ -416,10 +416,9 @@ static const struct dvb_frontend_ops lgs8gl5_ops = { .delsys = { SYS_DTMB }, .info = { .name = "Legend Silicon LGS-8GL5 DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, - .frequency_tolerance = 0, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c index 84af8a12f26a..a6bcf1571d10 100644 --- a/drivers/media/dvb-frontends/lgs8gxx.c +++ b/drivers/media/dvb-frontends/lgs8gxx.c @@ -985,9 +985,9 @@ static const struct dvb_frontend_ops lgs8gxx_ops = { .delsys = { SYS_DTMB }, .info = { .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 65d157fe76d1..dffd2d4bf1c8 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1300,9 +1300,9 @@ static const struct dvb_frontend_ops m88ds3103_ops = { .delsys = {SYS_DVBS, SYS_DVBS2}, .info = { .name = "Montage Technology M88DS3103", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 496ce27fa63a..d5bc85501f9e 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -759,10 +759,10 @@ static const struct dvb_frontend_ops m88rs2000_ops = { .delsys = { SYS_DVBS }, .info = { .name = "M88RS2000 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 377cd984b069..da505a5d035f 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1808,10 +1808,9 @@ static const struct dvb_frontend_ops mb86a16_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Fujitsu MB86A16 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 3000, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 3 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c3b1b88e2e7a..66fc77db0e75 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -2111,9 +2111,9 @@ static const struct dvb_frontend_ops mb86a20s_ops = { FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, /* Actually, those values depend on the used tuner */ - .frequency_min = 45000000, - .frequency_max = 864000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_stepsize_hz = 62500, }, .release = mb86a20s_release, diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index e2a3fc521620..aad07adda37d 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -559,8 +559,8 @@ static int mt312_set_frontend(struct dvb_frontend *fe) dprintk("%s: Freq %d\n", __func__, p->frequency); - if ((p->frequency < fe->ops.info.frequency_min) - || (p->frequency > fe->ops.info.frequency_max)) + if ((p->frequency < fe->ops.info.frequency_min_hz / kHz) + || (p->frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; if (((int)p->inversion < INVERSION_OFF) @@ -755,10 +755,10 @@ static const struct dvb_frontend_ops mt312_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Zarlink ???? DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, /* FIXME: adjust freq to real used xtal */ - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, + .frequency_stepsize_hz = MT312_PLL_CLK / 128, .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ .symbol_rate_max = MT312_SYS_CLK / 2, .caps = diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c index a440b76444d3..da3e466d50e2 100644 --- a/drivers/media/dvb-frontends/mt352.c +++ b/drivers/media/dvb-frontends/mt352.c @@ -567,10 +567,9 @@ static const struct dvb_frontend_ops mt352_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Zarlink MT352 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c index 274d8fca0763..295f37d5f10e 100644 --- a/drivers/media/dvb-frontends/mxl5xx.c +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -784,10 +784,8 @@ static struct dvb_frontend_ops mxl_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "MaxLinear MxL5xx DVB-S/S2 tuner-demodulator", - .frequency_min = 300000, - .frequency_max = 2350000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 300 * MHz, + .frequency_max_hz = 2350 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index a6cc4952eb74..0961e686ff68 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -1212,9 +1212,9 @@ static const struct dvb_frontend_ops nxt200x_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Nextwave NXT200X VSB/QAM frontend", - .frequency_min = 54000000, - .frequency_max = 860000000, - .frequency_stepsize = 166666, /* stepsize is just a guess */ + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, /* stepsize is just a guess */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256 diff --git a/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c index 109a635d166a..72e447e8ba64 100644 --- a/drivers/media/dvb-frontends/nxt6000.c +++ b/drivers/media/dvb-frontends/nxt6000.c @@ -596,9 +596,9 @@ static const struct dvb_frontend_ops nxt6000_ops = { .delsys = { SYS_DVBT }, .info = { .name = "NxtWave NXT6000 DVB-T", - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, /*.frequency_tolerance = *//* FIXME: 12% of SR */ .symbol_rate_min = 0, /* FIXME */ .symbol_rate_max = 9360000, /* FIXME */ diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index b4c9aadcb552..fc35f37eb3c0 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -583,9 +583,9 @@ static const struct dvb_frontend_ops or51132_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Oren OR51132 VSB/QAM Frontend", - .frequency_min = 44000000, - .frequency_max = 958000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index b65ba34fd00a..a39bbd8ff1f0 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -530,10 +530,10 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config, static const struct dvb_frontend_ops or51211_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { - .name = "Oren OR51211 VSB Frontend", - .frequency_min = 44000000, - .frequency_max = 958000000, - .frequency_stepsize = 166666, + .name = "Oren OR51211 VSB Frontend", + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_8VSB diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 7bbfe11d11ed..adc9046d5a90 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -159,8 +159,8 @@ static int rtl2830_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index fa3b8169c1a5..2f1f5cbaf03c 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -408,8 +408,8 @@ static int rtl2832_get_tune_settings(struct dvb_frontend *fe, dev_dbg(&client->dev, "\n"); s->min_delay_ms = 1000; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } @@ -841,9 +841,9 @@ static const struct dvb_frontend_ops rtl2832_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Realtek RTL2832 (DVB-T)", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index a23ba8727218..ceeb0c3551ce 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -999,9 +999,9 @@ static const struct dvb_frontend_ops s5h1409_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Samsung S5H1409 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index af5962807f2c..98aeed1d2284 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -918,9 +918,9 @@ static const struct dvb_frontend_ops s5h1411_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Samsung S5H1411 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c index 8b2222530227..a65cdf8e8cd9 100644 --- a/drivers/media/dvb-frontends/s5h1420.c +++ b/drivers/media/dvb-frontends/s5h1420.c @@ -934,10 +934,10 @@ static const struct dvb_frontend_ops s5h1420_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c index 740a60df0455..4dc3febc0e12 100644 --- a/drivers/media/dvb-frontends/s5h1432.c +++ b/drivers/media/dvb-frontends/s5h1432.c @@ -370,9 +370,9 @@ static const struct dvb_frontend_ops s5h1432_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Samsung s5h1432 DVB-T Frontend", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c index 6c9015236655..79276871112a 100644 --- a/drivers/media/dvb-frontends/s921.c +++ b/drivers/media/dvb-frontends/s921.c @@ -510,15 +510,14 @@ static const struct dvb_frontend_ops s921_ops = { /* Use dib8000 values per default */ .info = { .name = "Sharp S921", - .frequency_min = 470000000, + .frequency_min_hz = 470 * MHz, /* * Max should be 770MHz instead, according with Sharp docs, * but Leadership doc says it works up to 806 MHz. This is * required to get channel 69, used in Brazil */ - .frequency_max = 806000000, - .frequency_tolerance = 0, - .caps = FE_CAN_INVERSION_AUTO | + .frequency_max_hz = 806 * MHz, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 2dd336f95cbf..feacd8da421d 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1120,7 +1120,7 @@ static const struct dvb_frontend_ops si2165_ops = { .symbol_rate_min = 1000000, .symbol_rate_max = 7200000, /* For DVB-T */ - .frequency_stepsize = 166667, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index 9b32a1b3205e..8546a236d452 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -870,10 +870,9 @@ static const struct dvb_frontend_ops si21xx_ops = { .delsys = { SYS_DVBS }, .info = { .name = "SL SI21XX DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c index 1d57a20093fc..8d31cf3f4f07 100644 --- a/drivers/media/dvb-frontends/sp8870.c +++ b/drivers/media/dvb-frontends/sp8870.c @@ -584,9 +584,9 @@ static const struct dvb_frontend_ops sp8870_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Spase SP8870 DVB-T", - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c index 57a0d0ae2b48..c02f50995df4 100644 --- a/drivers/media/dvb-frontends/sp887x.c +++ b/drivers/media/dvb-frontends/sp887x.c @@ -594,9 +594,9 @@ static const struct dvb_frontend_ops sp887x_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Spase SP887x DVB-T", - .frequency_min = 50500000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 50500 * kHz, + .frequency_max_hz = 858000 * kHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c index 3c654ae16e78..874e9c9125d6 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.c +++ b/drivers/media/dvb-frontends/stb0899_drv.c @@ -1584,10 +1584,8 @@ static const struct dvb_frontend_ops stb0899_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STB0899 Multistandard", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 5000000, .symbol_rate_max = 45000000, diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c index f947ed947aae..c9a9fa4e2c1b 100644 --- a/drivers/media/dvb-frontends/stv0288.c +++ b/drivers/media/dvb-frontends/stv0288.c @@ -533,10 +533,9 @@ static const struct dvb_frontend_ops stv0288_ops = { .delsys = { SYS_DVBS }, .info = { .name = "ST STV0288 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c index b823c04e24d3..9a9915f71483 100644 --- a/drivers/media/dvb-frontends/stv0297.c +++ b/drivers/media/dvb-frontends/stv0297.c @@ -694,9 +694,9 @@ static const struct dvb_frontend_ops stv0297_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "ST STV0297 DVB-C", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 633b90e6fe86..4f466394a16c 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -717,10 +717,9 @@ static const struct dvb_frontend_ops stv0299_ops = { .delsys = { SYS_DVBS }, .info = { .name = "ST STV0299 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 5435c908e298..5b91e740e135 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -1693,10 +1693,9 @@ static const struct dvb_frontend_ops stv0367ter_ops = { .delsys = { SYS_DVBT }, .info = { .name = "ST STV0367 DVB-T", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 15625, - .frequency_tolerance = 0, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 15625, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | @@ -2867,9 +2866,9 @@ static const struct dvb_frontend_ops stv0367cab_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "ST STV0367 DVB-C", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = 0x400 |/* FE_CAN_QAM_4 */ @@ -3273,10 +3272,9 @@ static const struct dvb_frontend_ops stv0367ddb_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBT }, .info = { .name = "ST STV0367 DDB DVB-C/T", - .frequency_min = 47000000, - .frequency_max = 865000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, + .frequency_stepsize_hz = 166667, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = /* DVB-C */ diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 72f17b97ca04..254618a06140 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1875,10 +1875,9 @@ static const struct dvb_frontend_ops stv0900_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STV0900 frontend", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 9133f65d4623..a0622bb71803 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -4905,10 +4905,8 @@ static const struct dvb_frontend_ops stv090x_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STV090x Multistandard", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index 91b21eb59531..4c86073f1a8d 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -1724,10 +1724,8 @@ static const struct dvb_frontend_ops stv0910_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "ST STV0910", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 100000, .symbol_rate_max = 70000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index 7abf6b0916ed..2ad81a438d6a 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -714,8 +714,8 @@ static const struct dvb_frontend_ops tc90522_ops_sat = { .delsys = { SYS_ISDBS }, .info = { .name = "Toshiba TC90522 ISDB-S module", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, @@ -734,9 +734,9 @@ static const struct dvb_frontend_ops tc90522_ops_ter = { .delsys = { SYS_ISDBT }, .info = { .name = "Toshiba TC90522 ISDB-T module", - .frequency_min = 470000000, - .frequency_max = 770000000, - .frequency_stepsize = 142857, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 770 * MHz, + .frequency_stepsize_hz = 142857, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c index 4f588ebde39d..5cd885d4ea04 100644 --- a/drivers/media/dvb-frontends/tda10021.c +++ b/drivers/media/dvb-frontends/tda10021.c @@ -487,11 +487,11 @@ static const struct dvb_frontend_ops tda10021_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, .info = { .name = "Philips TDA10021 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, - .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = (XIN / 2) / 64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (XIN / 2) / 4, /* SACLK/4 */ #if 0 .frequency_tolerance = ???, .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ diff --git a/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c index 6c84916234e3..0a9a54563ebe 100644 --- a/drivers/media/dvb-frontends/tda10023.c +++ b/drivers/media/dvb-frontends/tda10023.c @@ -575,9 +575,9 @@ static const struct dvb_frontend_ops tda10023_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, .info = { .name = "Philips TDA10023 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 0, /* set in tda10023_attach */ .symbol_rate_max = 0, /* set in tda10023_attach */ .caps = 0x400 | //FE_CAN_QAM_4 diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c index de82a2558e15..c01d60a88af2 100644 --- a/drivers/media/dvb-frontends/tda10048.c +++ b/drivers/media/dvb-frontends/tda10048.c @@ -1156,9 +1156,9 @@ static const struct dvb_frontend_ops tda10048_ops = { .delsys = { SYS_DVBT }, .info = { .name = "NXP TDA10048HN DVB-T", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c index 7dcfb4a4b2d0..d402e4b722ca 100644 --- a/drivers/media/dvb-frontends/tda1004x.c +++ b/drivers/media/dvb-frontends/tda1004x.c @@ -1249,9 +1249,9 @@ static const struct dvb_frontend_ops tda10045_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Philips TDA10045H DVB-T", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | @@ -1319,9 +1319,9 @@ static const struct dvb_frontend_ops tda10046_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Philips TDA10046H DVB-T", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 1ed67c08e699..097c42d3f8c2 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -681,8 +681,8 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) cmd.args[5] = (c->frequency >> 0) & 0xff; cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff; cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff; - cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; - cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; + cmd.args[8] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 8) & 0xff; + cmd.args[9] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 0) & 0xff; cmd.args[10] = rolloff; cmd.args[11] = inversion; cmd.args[12] = pilot; @@ -1106,9 +1106,9 @@ static const struct dvb_frontend_ops tda10071_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "NXP TDA10071", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c index 1a95c521e97f..8323e4e53d66 100644 --- a/drivers/media/dvb-frontends/tda10086.c +++ b/drivers/media/dvb-frontends/tda10086.c @@ -710,9 +710,9 @@ static const struct dvb_frontend_ops tda10086_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Philips TDA10086 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c index 29b4f64c030c..53b26060db7e 100644 --- a/drivers/media/dvb-frontends/tda8083.c +++ b/drivers/media/dvb-frontends/tda8083.c @@ -453,10 +453,9 @@ static const struct dvb_frontend_ops tda8083_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Philips TDA8083 DVB-S", - .frequency_min = 920000, /* TDA8060 */ - .frequency_max = 2200000, /* TDA8060 */ - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - /* .frequency_tolerance = ???,*/ + .frequency_min_hz = 920 * MHz, /* TDA8060 */ + .frequency_max_hz = 2200 * MHz, /* TDA8060 */ + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 12000000, .symbol_rate_max = 30000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c index 17600989f121..eb1249d81310 100644 --- a/drivers/media/dvb-frontends/ves1820.c +++ b/drivers/media/dvb-frontends/ves1820.c @@ -412,9 +412,9 @@ static const struct dvb_frontend_ops ves1820_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "VLSI VES1820 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c index 0c7b3286b04d..ddc5bfd84cd5 100644 --- a/drivers/media/dvb-frontends/ves1x93.c +++ b/drivers/media/dvb-frontends/ves1x93.c @@ -516,10 +516,10 @@ static const struct dvb_frontend_ops ves1x93_ops = { .delsys = { SYS_DVBS }, .info = { .name = "VLSI VES1x93 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c index e5a432fd84c3..f1c92338015d 100644 --- a/drivers/media/dvb-frontends/zl10036.c +++ b/drivers/media/dvb-frontends/zl10036.c @@ -311,8 +311,8 @@ static int zl10036_set_params(struct dvb_frontend *fe) /* ensure correct values * maybe redundant as core already checks this */ - if ((frequency < fe->ops.info.frequency_min) - || (frequency > fe->ops.info.frequency_max)) + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; /* diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c index c9901f45deb7..42e63a3fa121 100644 --- a/drivers/media/dvb-frontends/zl10353.c +++ b/drivers/media/dvb-frontends/zl10353.c @@ -635,10 +635,9 @@ static const struct dvb_frontend_ops zl10353_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Zarlink ZL10353 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/firewire/firedtv-fe.c b/drivers/media/firewire/firedtv-fe.c index a2ef4ede8ebe..69087ae6c1d0 100644 --- a/drivers/media/firewire/firedtv-fe.c +++ b/drivers/media/firewire/firedtv-fe.c @@ -152,7 +152,7 @@ static int fdtv_set_frontend(struct dvb_frontend *fe) void fdtv_frontend_init(struct firedtv *fdtv, const char *name) { struct dvb_frontend_ops *ops = &fdtv->fe.ops; - struct dvb_frontend_info *fi = &ops->info; + struct dvb_frontend_internal_info *fi = &ops->info; ops->init = fdtv_dvb_init; ops->sleep = fdtv_sleep; @@ -174,9 +174,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_S: ops->delsys[0] = SYS_DVBS; - fi->frequency_min = 950000; - fi->frequency_max = 2150000; - fi->frequency_stepsize = 125; + fi->frequency_min_hz = 950 * MHz; + fi->frequency_max_hz = 2150 * MHz; + fi->frequency_stepsize_hz = 125 * kHz; fi->symbol_rate_min = 1000000; fi->symbol_rate_max = 40000000; @@ -194,9 +194,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) ops->delsys[0] = SYS_DVBS; ops->delsys[1] = SYS_DVBS2; - fi->frequency_min = 950000; - fi->frequency_max = 2150000; - fi->frequency_stepsize = 125; + fi->frequency_min_hz = 950 * MHz; + fi->frequency_max_hz = 2150 * MHz; + fi->frequency_stepsize_hz = 125 * kHz; fi->symbol_rate_min = 1000000; fi->symbol_rate_max = 40000000; @@ -214,9 +214,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_C: ops->delsys[0] = SYS_DVBC_ANNEX_A; - fi->frequency_min = 47000000; - fi->frequency_max = 866000000; - fi->frequency_stepsize = 62500; + fi->frequency_min_hz = 47 * MHz; + fi->frequency_max_hz = 866 * MHz; + fi->frequency_stepsize_hz = 62500; fi->symbol_rate_min = 870000; fi->symbol_rate_max = 6900000; @@ -232,9 +232,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_T: ops->delsys[0] = SYS_DVBT; - fi->frequency_min = 49000000; - fi->frequency_max = 861000000; - fi->frequency_stepsize = 62500; + fi->frequency_min_hz = 49 * MHz; + fi->frequency_max_hz = 861 * MHz; + fi->frequency_stepsize_hz = 62500; fi->caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_2_3 | diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c index 2e33b7236672..b98de2a22f78 100644 --- a/drivers/media/pci/bt8xx/dst.c +++ b/drivers/media/pci/bt8xx/dst.c @@ -1739,9 +1739,9 @@ static const struct dvb_frontend_ops dst_dvbt_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DST DVB-T", - .frequency_min = 137000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 137 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_16 | @@ -1768,10 +1768,10 @@ static const struct dvb_frontend_ops dst_dvbs_ops = { .delsys = { SYS_DVBS }, .info = { .name = "DST DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* . symbol_rate_tolerance = ???,*/ @@ -1797,9 +1797,9 @@ static const struct dvb_frontend_ops dst_dvbc_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "DST DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_FEC_AUTO | @@ -1826,9 +1826,9 @@ static const struct dvb_frontend_ops dst_atsc_ops = { .delsys = { SYS_ATSC }, .info = { .name = "DST ATSC", - .frequency_stepsize = 62500, - .frequency_min = 510000000, - .frequency_max = 858000000, + .frequency_min_hz = 510 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 6b2a9e6eb6a8..2f810b7130e6 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -602,8 +602,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; + card->fe->ops.info.frequency_min_hz = 174 * MHz; + card->fe->ops.info.frequency_max_hz = 862 * MHz; } break; @@ -655,8 +655,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; + card->fe->ops.info.frequency_min_hz = 174 * MHz; + card->fe->ops.info.frequency_max_hz = 862 * MHz; } break; diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c index 4418604258d1..64f05f5ee039 100644 --- a/drivers/media/pci/ddbridge/ddbridge-sx8.c +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -454,10 +454,8 @@ static struct dvb_frontend_ops sx8_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 100000, .symbol_rate_max = 100000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c index 14f6e153000c..9797c9fd8259 100644 --- a/drivers/media/pci/mantis/mantis_vp3030.c +++ b/drivers/media/pci/mantis/mantis_vp3030.c @@ -42,8 +42,8 @@ static struct zl10353_config mantis_vp3030_config = { static struct tda665x_config env57h12d5_config = { .name = "ENV57H12D5 (ET-50DT)", .addr = 0x60, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min = 47 * MHz, + .frequency_max = 862 * MHz, .frequency_offst = 3616667, .ref_multiplier = 6, /* 1/6 MHz */ .ref_divider = 100000, /* 1/6 MHz */ diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 4081fd97c3b2..54d9226aaff7 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -59,8 +59,6 @@ MODULE_PARM_DESC(debug, "set debug level"); /* ------------------------------------------------------------------------- */ -#define MHz 1000000 - enum mxl5007t_mode { MxL_MODE_ISDBT = 0, MxL_MODE_DVBT = 1, diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index 221cf46b4140..9f74453799a2 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -554,9 +554,9 @@ static const struct dvb_frontend_ops mxl111sf_demod_ops = { .delsys = { SYS_DVBT }, .info = { .name = "MaxLinear MxL111SF DVB-T demodulator", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c index 7fbbc954da16..09cc3a21af65 100644 --- a/drivers/media/usb/dvb-usb/af9005-fe.c +++ b/drivers/media/usb/dvb-usb/af9005-fe.c @@ -1455,9 +1455,9 @@ static const struct dvb_frontend_ops af9005_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "AF9005 USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c index 5a2f81311fb7..df71df7ed524 100644 --- a/drivers/media/usb/dvb-usb/cinergyT2-fe.c +++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c @@ -295,9 +295,9 @@ static const struct dvb_frontend_ops cinergyt2_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = DRIVER_NAME, - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 diff --git a/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c index 7e75aae34fb8..1ca3a51b2ae3 100644 --- a/drivers/media/usb/dvb-usb/dtt200u-fe.c +++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c @@ -230,9 +230,9 @@ static const struct dvb_frontend_ops dtt200u_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "WideView USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c index 932f262452eb..e6bd0ed8d789 100644 --- a/drivers/media/usb/dvb-usb/friio-fe.c +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -133,10 +133,10 @@ static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) u32 f; deb_fe("%s: freq=%d, step=%d\n", __func__, freq, - state->frontend.ops.info.frequency_stepsize); + state->frontend.ops.info.frequency_stepsize_hz); /* freq -> oscilator frequency conversion. */ /* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */ - f = freq / state->frontend.ops.info.frequency_stepsize; + f = freq / state->frontend.ops.info.frequency_stepsize_hz; /* add 399[1/7 MHZ] = 57MHz for the IF */ f += 399; /* add center frequency shift if necessary */ @@ -413,10 +413,9 @@ static const struct dvb_frontend_ops jdvbt90502_ops = { .delsys = { SYS_ISDBT }, .info = { .name = "Comtech JDVBT90502 ISDB-T", - .frequency_min = 473000000, /* UHF 13ch, center */ - .frequency_max = 767142857, /* UHF 62ch, center */ - .frequency_stepsize = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, - .frequency_tolerance = 0, + .frequency_min_hz = 473000000, /* UHF 13ch, center */ + .frequency_max_hz = 767142857, /* UHF 62ch, center */ + .frequency_stepsize_hz = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, /* NOTE: this driver ignores all parameters but frequency. */ .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c index ae48146e005c..9eb811452f2e 100644 --- a/drivers/media/usb/dvb-usb/vp702x-fe.c +++ b/drivers/media/usb/dvb-usb/vp702x-fe.c @@ -349,10 +349,9 @@ static const struct dvb_frontend_ops vp702x_fe_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c index f86040173b8d..1173ae29885b 100644 --- a/drivers/media/usb/dvb-usb/vp7045-fe.c +++ b/drivers/media/usb/dvb-usb/vp7045-fe.c @@ -162,9 +162,9 @@ static const struct dvb_frontend_ops vp7045_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Twinhan VP7045/46 USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 1000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 1 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c index 6ea05d909024..278bf6c5855b 100644 --- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -247,9 +247,9 @@ static const struct dvb_frontend_ops ttusbdecfe_dvbt_ops = { .delsys = { SYS_DVBT }, .info = { .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | @@ -270,9 +270,9 @@ static const struct dvb_frontend_ops ttusbdecfe_dvbs_ops = { .delsys = { SYS_DVBS }, .info = { .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, /* guessed */ .symbol_rate_max = 45000000, /* guessed */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/include/media/dvb_frontend.h b/include/media/dvb_frontend.h index aebaec2dc725..6f7a85ab3541 100644 --- a/include/media/dvb_frontend.h +++ b/include/media/dvb_frontend.h @@ -317,6 +317,34 @@ struct analog_demod_ops { struct dtv_frontend_properties; +/** + * struct dvb_frontend_internal_info - Frontend properties and capabilities + * + * @name: Name of the frontend + * @frequency_min_hz: Minimal frequency supported by the frontend. + * @frequency_max_hz: Minimal frequency supported by the frontend. + * @frequency_stepsize_hz: All frequencies are multiple of this value. + * @frequency_tolerance_hz: Frequency tolerance. + * @symbol_rate_min: Minimal symbol rate, in bauds + * (for Cable/Satellite systems). + * @symbol_rate_max: Maximal symbol rate, in bauds + * (for Cable/Satellite systems). + * @symbol_rate_tolerance: Maximal symbol rate tolerance, in ppm + * (for Cable/Satellite systems). + * @caps: Capabilities supported by the frontend, + * as specified in &enum fe_caps. + */ +struct dvb_frontend_internal_info { + char name[128]; + u32 frequency_min_hz; + u32 frequency_max_hz; + u32 frequency_stepsize_hz; + u32 frequency_tolerance_hz; + u32 symbol_rate_min; + u32 symbol_rate_max; + u32 symbol_rate_tolerance; + enum fe_caps caps; +}; /** * struct dvb_frontend_ops - Demodulation information and callbacks for @@ -404,7 +432,7 @@ struct dtv_frontend_properties; * @analog_ops: pointer to &struct analog_demod_ops */ struct dvb_frontend_ops { - struct dvb_frontend_info info; + struct dvb_frontend_internal_info info; u8 delsys[MAX_DELSYS]; -- cgit v1.2.3-70-g09d2 From 6a2a1ca34ca68025df793a1bedfba658be2b1952 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2018 18:59:37 -0400 Subject: media: dvb_frontend: ensure that the step is ok for both FE and tuner The frequency step should take into account the tuner step, as, if tuner step is bigger than frontend step, the zigzag algorithm won't be doing the right thing, as it will be tuning multiple times at the same frequency. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index fe0fae482ba4..c4e7ebfe4d29 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -939,7 +939,10 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, static u32 dvb_frontend_get_stepsize(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 step = fe->ops.info.frequency_stepsize_hz; + u32 fe_step = fe->ops.info.frequency_stepsize_hz; + u32 tuner_step = fe->ops.tuner_ops.info.frequency_step_hz; + u32 step = max(fe_step, tuner_step); + switch (c->delivery_system) { case SYS_DVBS: case SYS_DVBS2: -- cgit v1.2.3-70-g09d2 From 1b7369acc465ddf3d9c7604af3971753b9a548f2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 9 Jul 2018 11:19:02 -0400 Subject: media: headers: fix linux/mod_devicetable.h inclusions A couple of drivers produced build errors after the mod_devicetable.h header was split out from the platform_device one, e.g. drivers/media/platform/davinci/vpbe_osd.c:42:40: error: array type has incomplete element type 'struct platform_device_id' drivers/media/platform/davinci/vpbe_venc.c:42:40: error: array type has incomplete element type 'struct platform_device_id' This adds the inclusion where needed. Fixes: ac3167257b9f ("headers: separate linux/mod_devicetable.h from linux/platform_device.h") Signed-off-by: Arnd Bergmann Acked-by: Michael S. Tsirkin Acked-by: Randy Dunlap Acked-by: Lad, Prabhakar Acked-by: Andy Shevchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/firmware/qemu_fw_cfg.c | 1 + drivers/media/platform/davinci/vpbe_osd.c | 1 + drivers/media/platform/davinci/vpbe_venc.c | 1 + drivers/media/platform/qcom/venus/vdec.c | 1 + drivers/media/platform/qcom/venus/venc.c | 1 + drivers/media/platform/sti/hva/hva-v4l2.c | 1 + drivers/platform/x86/intel_punit_ipc.c | 1 + 7 files changed, 7 insertions(+) (limited to 'drivers/media') diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 14fedbeca724..039e0f91dba8 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -28,6 +28,7 @@ */ #include +#include #include #include #include diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 7f610320426d..c551a25d90d9 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -18,6 +18,7 @@ * */ #include +#include #include #include #include diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index ba157827192c..ddcad7b3e76c 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ #include +#include #include #include #include diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 0d043c33cd83..dfbbbf0f746f 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -14,6 +14,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 20d7246a3ef9..41249d1443fa 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -14,6 +14,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 15080cb00fa7..5a807c7c5e79 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index b5b890127479..f1afc0ebbc68 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3-70-g09d2 From 299c7007e93645067e1d2743f4e50156de78c4ff Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Mon, 23 Jul 2018 13:04:54 -0400 Subject: media: dw2102: Fix memleak on sequence of probes Each call to dw2102_probe() allocates memory by kmemdup for structures p1100, s660, p7500 and s421, but there is no their deallocation. dvb_usb_device_init() copies the corresponding structure into dvb_usb_device->props, so there is no use of original structure after dvb_usb_device_init(). The patch moves structures from global scope to local and adds their deallocation. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 0d4fdd34a710..9ce8b4d79d1f 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -2101,14 +2101,12 @@ static struct dvb_usb_device_properties s6x0_properties = { } }; -static struct dvb_usb_device_properties *p1100; static const struct dvb_usb_device_description d1100 = { "Prof 1100 USB ", {&dw2102_table[PROF_1100], NULL}, {NULL}, }; -static struct dvb_usb_device_properties *s660; static const struct dvb_usb_device_description d660 = { "TeVii S660 USB", {&dw2102_table[TEVII_S660], NULL}, @@ -2127,14 +2125,12 @@ static const struct dvb_usb_device_description d480_2 = { {NULL}, }; -static struct dvb_usb_device_properties *p7500; static const struct dvb_usb_device_description d7500 = { "Prof 7500 USB DVB-S2", {&dw2102_table[PROF_7500], NULL}, {NULL}, }; -static struct dvb_usb_device_properties *s421; static const struct dvb_usb_device_description d421 = { "TeVii S421 PCI", {&dw2102_table[TEVII_S421], NULL}, @@ -2334,6 +2330,11 @@ static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { int retval = -ENOMEM; + struct dvb_usb_device_properties *p1100; + struct dvb_usb_device_properties *s660; + struct dvb_usb_device_properties *p7500; + struct dvb_usb_device_properties *s421; + p1100 = kmemdup(&s6x0_properties, sizeof(struct dvb_usb_device_properties), GFP_KERNEL); if (!p1100) @@ -2402,8 +2403,16 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, &t220_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &tt_s2_4600_properties, - THIS_MODULE, NULL, adapter_nr)) + THIS_MODULE, NULL, adapter_nr)) { + + /* clean up copied properties */ + kfree(s421); + kfree(p7500); + kfree(s660); + kfree(p1100); + return 0; + } retval = -ENODEV; kfree(s421); -- cgit v1.2.3-70-g09d2 From 817dc4b579d897d3f645c11dc255f21642aa99df Mon Sep 17 00:00:00 2001 From: Katsuhiro Suzuki Date: Tue, 17 Jul 2018 21:06:42 -0400 Subject: media: helene: add I2C device probe function This patch adds I2C probe function to use dvb_module_probe() with this driver. And also support multiple delivery systems at the same device. Signed-off-by: Katsuhiro Suzuki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/helene.c | 88 ++++++++++++++++++++++++++++++++++-- drivers/media/dvb-frontends/helene.h | 3 ++ 2 files changed, 87 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index dc70fb684681..d7790cb98a0c 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -666,7 +666,7 @@ static int helene_set_params_s(struct dvb_frontend *fe) return 0; } -static int helene_set_params(struct dvb_frontend *fe) +static int helene_set_params_t(struct dvb_frontend *fe) { u8 data[MAX_WRITE_REGSIZE]; u32 frequency; @@ -835,6 +835,19 @@ static int helene_set_params(struct dvb_frontend *fe) return 0; } +static int helene_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->delivery_system == SYS_DVBT || + p->delivery_system == SYS_DVBT2 || + p->delivery_system == SYS_ISDBT || + p->delivery_system == SYS_DVBC_ANNEX_A) + return helene_set_params_t(fe); + + return helene_set_params_s(fe); +} + static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct helene_priv *priv = fe->tuner_priv; @@ -843,7 +856,7 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static const struct dvb_tuner_ops helene_tuner_ops = { +static const struct dvb_tuner_ops helene_tuner_ops_t = { .info = { .name = "Sony HELENE Ter tuner", .frequency_min_hz = 1 * MHz, @@ -853,7 +866,7 @@ static const struct dvb_tuner_ops helene_tuner_ops = { .init = helene_init, .release = helene_release, .sleep = helene_sleep, - .set_params = helene_set_params, + .set_params = helene_set_params_t, .get_frequency = helene_get_frequency, }; @@ -871,6 +884,20 @@ static const struct dvb_tuner_ops helene_tuner_ops_s = { .get_frequency = helene_get_frequency, }; +static const struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Sat/Ter tuner", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 25 * kHz, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + /* power-on tuner * call once after reset */ @@ -1035,7 +1062,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = priv; dev_info(&priv->i2c->dev, @@ -1045,6 +1072,59 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, } EXPORT_SYMBOL(helene_attach); +static int helene_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct helene_config *config = client->dev.platform_data; + struct dvb_frontend *fe = config->fe; + struct device *dev = &client->dev; + struct helene_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->i2c_address = client->addr; + priv->i2c = client->adapter; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + i2c_set_clientdata(client, priv); + + dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + + return 0; +} + +static const struct i2c_device_id helene_id[] = { + { "helene", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, helene_id); + +static struct i2c_driver helene_driver = { + .driver = { + .name = "helene", + }, + .probe = helene_probe, + .id_table = helene_id, +}; +module_i2c_driver(helene_driver); + MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); MODULE_AUTHOR("Abylay Ospan "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h index c9fc81c7e4e7..8562d01bc93e 100644 --- a/drivers/media/dvb-frontends/helene.h +++ b/drivers/media/dvb-frontends/helene.h @@ -39,6 +39,7 @@ enum helene_xtal { * @set_tuner_callback: Callback function that notifies the parent driver * which tuner is active now * @xtal: Cristal frequency as described by &enum helene_xtal + * @fe: Frontend for which connects this tuner */ struct helene_config { u8 i2c_address; @@ -46,6 +47,8 @@ struct helene_config { void *set_tuner_priv; int (*set_tuner_callback)(void *, int); enum helene_xtal xtal; + + struct dvb_frontend *fe; }; #if IS_REACHABLE(CONFIG_DVB_HELENE) -- cgit v1.2.3-70-g09d2 From 65b40a986fc68cb3b362bb855303b73a2b8538c2 Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Wed, 18 Jul 2018 10:13:56 -0400 Subject: media: dm1105: Limit number of cards to avoid buffer over read dm1105_probe() counts number of cards at dm1105_devcount, but missed bounds check before dereference a card array. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/dm1105/dm1105.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index c9db108751a7..1ddb0576fb7b 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -986,6 +986,9 @@ static int dm1105_probe(struct pci_dev *pdev, int ret = -ENOMEM; int i; + if (dm1105_devcount >= ARRAY_SIZE(card)) + return -ENODEV; + dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); if (!dev) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 0f408ce8941fcb1b6e8431272cfc9337a0407d73 Mon Sep 17 00:00:00 2001 From: Katsuhiro Suzuki Date: Mon, 23 Jul 2018 04:51:50 -0400 Subject: media: dvb-frontends: add Socionext MN88443x ISDB-S/T demodulator driver This patch adds a frontend driver for the Socionext/Panasonic MN884434 and MN884433 ISDB-S/T demodulators. The maximum and minimum frequency of MN88443x comes from ISDB-S and ISDB-T so frequency range is the following: - ISDB-S (BS/CS110 IF frequency, Local freq 10.678GHz) - Min: BS-1: 1032MHz - Max: ND24: 2070MHz - ISDB-T - Min: ch13: 470MHz - Max: ch62: 770MHz Signed-off-by: Katsuhiro Suzuki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 10 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mn88443x.c | 802 +++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/mn88443x.h | 27 ++ 4 files changed, 840 insertions(+) create mode 100644 drivers/media/dvb-frontends/mn88443x.c create mode 100644 drivers/media/dvb-frontends/mn88443x.h (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 9ecaa9d0744a..048285134cdf 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -739,6 +739,16 @@ config DVB_TC90522 Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator. Say Y when you want to support this frontend. +config DVB_MN88443X + tristate "Socionext MN88443x" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for Socionext/Panasonic MN884433 and MN884434 + ISDB-S + ISDB-T demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 67a783fd5ed0..779dfd027b24 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -125,6 +125,7 @@ obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o +obj-$(CONFIG_DVB_MN88443X) += mn88443x.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o obj-$(CONFIG_DVB_HELENE) += helene.o diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c new file mode 100644 index 000000000000..9ec1aeef03d5 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -0,0 +1,802 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. +// +// Copyright (c) 2018 Socionext Inc. + +#include +#include +#include +#include +#include +#include +#include + +#include "mn88443x.h" + +/* ISDB-S registers */ +#define ATSIDU_S 0x2f +#define ATSIDL_S 0x30 +#define TSSET_S 0x31 +#define AGCREAD_S 0x5a +#define CPMON1_S 0x5e +#define CPMON1_S_FSYNC BIT(5) +#define CPMON1_S_ERRMON BIT(4) +#define CPMON1_S_SIGOFF BIT(3) +#define CPMON1_S_W2LOCK BIT(2) +#define CPMON1_S_W1LOCK BIT(1) +#define CPMON1_S_DW1LOCK BIT(0) +#define TRMON_S 0x60 +#define BERCNFLG_S 0x68 +#define BERCNFLG_S_BERVRDY BIT(5) +#define BERCNFLG_S_BERVCHK BIT(4) +#define BERCNFLG_S_BERDRDY BIT(3) +#define BERCNFLG_S_BERDCHK BIT(2) +#define CNRDXU_S 0x69 +#define CNRDXL_S 0x6a +#define CNRDYU_S 0x6b +#define CNRDYL_S 0x6c +#define BERVRDU_S 0x71 +#define BERVRDL_S 0x72 +#define DOSET1_S 0x73 + +/* Primary ISDB-T */ +#define PLLASET1 0x00 +#define PLLASET2 0x01 +#define PLLBSET1 0x02 +#define PLLBSET2 0x03 +#define PLLSET 0x04 +#define OUTCSET 0x08 +#define OUTCSET_CHDRV_8MA 0xff +#define OUTCSET_CHDRV_4MA 0x00 +#define PLDWSET 0x09 +#define PLDWSET_NORMAL 0x00 +#define PLDWSET_PULLDOWN 0xff +#define HIZSET1 0x0a +#define HIZSET2 0x0b + +/* Secondary ISDB-T (for MN884434 only) */ +#define RCVSET 0x00 +#define TSSET1_M 0x01 +#define TSSET2_M 0x02 +#define TSSET3_M 0x03 +#define INTACSET 0x08 +#define HIZSET3 0x0b + +/* ISDB-T registers */ +#define TSSET1 0x05 +#define TSSET1_TSASEL_MASK GENMASK(4, 3) +#define TSSET1_TSASEL_ISDBT (0x0 << 3) +#define TSSET1_TSASEL_ISDBS (0x1 << 3) +#define TSSET1_TSASEL_NONE (0x2 << 3) +#define TSSET1_TSBSEL_MASK GENMASK(2, 1) +#define TSSET1_TSBSEL_ISDBS (0x0 << 1) +#define TSSET1_TSBSEL_ISDBT (0x1 << 1) +#define TSSET1_TSBSEL_NONE (0x2 << 1) +#define TSSET2 0x06 +#define TSSET3 0x07 +#define TSSET3_INTASEL_MASK GENMASK(7, 6) +#define TSSET3_INTASEL_T (0x0 << 6) +#define TSSET3_INTASEL_S (0x1 << 6) +#define TSSET3_INTASEL_NONE (0x2 << 6) +#define TSSET3_INTBSEL_MASK GENMASK(5, 4) +#define TSSET3_INTBSEL_S (0x0 << 4) +#define TSSET3_INTBSEL_T (0x1 << 4) +#define TSSET3_INTBSEL_NONE (0x2 << 4) +#define OUTSET2 0x0d +#define PWDSET 0x0f +#define PWDSET_OFDMPD_MASK GENMASK(3, 2) +#define PWDSET_OFDMPD_DOWN BIT(3) +#define PWDSET_PSKPD_MASK GENMASK(1, 0) +#define PWDSET_PSKPD_DOWN BIT(1) +#define CLKSET1_T 0x11 +#define MDSET_T 0x13 +#define MDSET_T_MDAUTO_MASK GENMASK(7, 4) +#define MDSET_T_MDAUTO_AUTO (0xf << 4) +#define MDSET_T_MDAUTO_MANUAL (0x0 << 4) +#define MDSET_T_FFTS_MASK GENMASK(3, 2) +#define MDSET_T_FFTS_MODE1 (0x0 << 2) +#define MDSET_T_FFTS_MODE2 (0x1 << 2) +#define MDSET_T_FFTS_MODE3 (0x2 << 2) +#define MDSET_T_GI_MASK GENMASK(1, 0) +#define MDSET_T_GI_1_32 (0x0 << 0) +#define MDSET_T_GI_1_16 (0x1 << 0) +#define MDSET_T_GI_1_8 (0x2 << 0) +#define MDSET_T_GI_1_4 (0x3 << 0) +#define MDASET_T 0x14 +#define ADCSET1_T 0x20 +#define ADCSET1_T_REFSEL_MASK GENMASK(1, 0) +#define ADCSET1_T_REFSEL_2V (0x3 << 0) +#define ADCSET1_T_REFSEL_1_5V (0x2 << 0) +#define ADCSET1_T_REFSEL_1V (0x1 << 0) +#define NCOFREQU_T 0x24 +#define NCOFREQM_T 0x25 +#define NCOFREQL_T 0x26 +#define FADU_T 0x27 +#define FADM_T 0x28 +#define FADL_T 0x29 +#define AGCSET2_T 0x2c +#define AGCSET2_T_IFPOLINV_INC BIT(0) +#define AGCSET2_T_RFPOLINV_INC BIT(1) +#define AGCV3_T 0x3e +#define MDRD_T 0xa2 +#define MDRD_T_SEGID_MASK GENMASK(5, 4) +#define MDRD_T_SEGID_13 (0x0 << 4) +#define MDRD_T_SEGID_1 (0x1 << 4) +#define MDRD_T_SEGID_3 (0x2 << 4) +#define MDRD_T_FFTS_MASK GENMASK(3, 2) +#define MDRD_T_FFTS_MODE1 (0x0 << 2) +#define MDRD_T_FFTS_MODE2 (0x1 << 2) +#define MDRD_T_FFTS_MODE3 (0x2 << 2) +#define MDRD_T_GI_MASK GENMASK(1, 0) +#define MDRD_T_GI_1_32 (0x0 << 0) +#define MDRD_T_GI_1_16 (0x1 << 0) +#define MDRD_T_GI_1_8 (0x2 << 0) +#define MDRD_T_GI_1_4 (0x3 << 0) +#define SSEQRD_T 0xa3 +#define SSEQRD_T_SSEQSTRD_MASK GENMASK(3, 0) +#define SSEQRD_T_SSEQSTRD_RESET (0x0 << 0) +#define SSEQRD_T_SSEQSTRD_TUNING (0x1 << 0) +#define SSEQRD_T_SSEQSTRD_AGC (0x2 << 0) +#define SSEQRD_T_SSEQSTRD_SEARCH (0x3 << 0) +#define SSEQRD_T_SSEQSTRD_CLOCK_SYNC (0x4 << 0) +#define SSEQRD_T_SSEQSTRD_FREQ_SYNC (0x8 << 0) +#define SSEQRD_T_SSEQSTRD_FRAME_SYNC (0x9 << 0) +#define SSEQRD_T_SSEQSTRD_SYNC (0xa << 0) +#define SSEQRD_T_SSEQSTRD_LOCK (0xb << 0) +#define AGCRDU_T 0xa8 +#define AGCRDL_T 0xa9 +#define CNRDU_T 0xbe +#define CNRDL_T 0xbf +#define BERFLG_T 0xc0 +#define BERFLG_T_BERDRDY BIT(7) +#define BERFLG_T_BERDCHK BIT(6) +#define BERFLG_T_BERVRDYA BIT(5) +#define BERFLG_T_BERVCHKA BIT(4) +#define BERFLG_T_BERVRDYB BIT(3) +#define BERFLG_T_BERVCHKB BIT(2) +#define BERFLG_T_BERVRDYC BIT(1) +#define BERFLG_T_BERVCHKC BIT(0) +#define BERRDU_T 0xc1 +#define BERRDM_T 0xc2 +#define BERRDL_T 0xc3 +#define BERLENRDU_T 0xc4 +#define BERLENRDL_T 0xc5 +#define ERRFLG_T 0xc6 +#define ERRFLG_T_BERDOVF BIT(7) +#define ERRFLG_T_BERVOVFA BIT(6) +#define ERRFLG_T_BERVOVFB BIT(5) +#define ERRFLG_T_BERVOVFC BIT(4) +#define ERRFLG_T_NERRFA BIT(3) +#define ERRFLG_T_NERRFB BIT(2) +#define ERRFLG_T_NERRFC BIT(1) +#define ERRFLG_T_NERRF BIT(0) +#define DOSET1_T 0xcf + +#define CLK_LOW 4000000 +#define CLK_DIRECT 20200000 +#define CLK_MAX 25410000 + +#define S_T_FREQ 8126984 /* 512 / 63 MHz */ + +struct mn88443x_spec { + bool primary; +}; + +struct mn88443x_priv { + const struct mn88443x_spec *spec; + + struct dvb_frontend fe; + struct clk *mclk; + struct gpio_desc *reset_gpio; + u32 clk_freq; + u32 if_freq; + + /* Common */ + bool use_clkbuf; + + /* ISDB-S */ + struct i2c_client *client_s; + struct regmap *regmap_s; + + /* ISDB-T */ + struct i2c_client *client_t; + struct regmap *regmap_t; +}; + +static void mn88443x_cmn_power_on(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + clk_prepare_enable(chip->mclk); + + gpiod_set_value_cansleep(chip->reset_gpio, 1); + usleep_range(100, 1000); + gpiod_set_value_cansleep(chip->reset_gpio, 0); + + if (chip->spec->primary) { + regmap_write(r_t, OUTCSET, OUTCSET_CHDRV_8MA); + regmap_write(r_t, PLDWSET, PLDWSET_NORMAL); + regmap_write(r_t, HIZSET1, 0x80); + regmap_write(r_t, HIZSET2, 0xe0); + } else { + regmap_write(r_t, HIZSET3, 0x8f); + } +} + +static void mn88443x_cmn_power_off(struct mn88443x_priv *chip) +{ + gpiod_set_value_cansleep(chip->reset_gpio, 1); + + clk_disable_unprepare(chip->mclk); +} + +static void mn88443x_s_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, + PWDSET_PSKPD_DOWN); +} + +static void mn88443x_s_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, 0); +} + +static void mn88443x_s_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_s = chip->regmap_s; + + regmap_write(r_s, ATSIDU_S, c->stream_id >> 8); + regmap_write(r_s, ATSIDL_S, c->stream_id); + regmap_write(r_s, TSSET_S, 0); +} + +static int mn88443x_s_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_s = chip->regmap_s; + u32 cpmon, tmpu, tmpl, flg; + u64 tmp; + + /* Sync detection */ + regmap_read(r_s, CPMON1_S, &cpmon); + + *status = 0; + if (cpmon & CPMON1_S_FSYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (cpmon & CPMON1_S_W2LOCK) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_s, AGCREAD_S, &tmpu); + agc = tmpu << 8; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr = 0, x, y, d; + u64 d_3 = 0; + + regmap_read(r_s, CNRDXU_S, &tmpu); + regmap_read(r_s, CNRDXL_S, &tmpl); + x = (tmpu << 8) | tmpl; + regmap_read(r_s, CNRDYU_S, &tmpu); + regmap_read(r_s, CNRDYL_S, &tmpl); + y = (tmpu << 8) | tmpl; + + /* CNR[dB]: 10 * log10(D) - 30.74 / D^3 - 3 */ + /* D = x^2 / (2^15 * y - x^2) */ + d = (y << 15) - x * x; + if (d > 0) { + /* (2^4 * D)^3 = 2^12 * D^3 */ + /* 3.074 * 2^(12 + 24) = 211243671486 */ + d_3 = div_u64(16 * x * x, d); + d_3 = d_3 * d_3 * d_3; + if (d_3) + d_3 = div_u64(211243671486ULL, d_3); + } + + if (d_3) { + /* 0.3 * 2^24 = 5033164 */ + tmp = (s64)2 * intlog10(x) - intlog10(abs(d)) - d_3 + - 5033164; + cnr = div_u64(tmp * 10000, 1 << 24); + } + + if (cnr) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_s, BERCNFLG_S, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERCNFLG_S_BERVRDY)) { + u32 bit_err, bit_cnt; + + regmap_read(r_s, BERVRDU_S, &tmpu); + regmap_read(r_s, BERVRDL_S, &tmpl); + bit_err = (tmpu << 8) | tmpl; + bit_cnt = (1 << 13) * 204; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static void mn88443x_t_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, + PWDSET_OFDMPD_DOWN); +} + +static void mn88443x_t_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, 0); +} + +static bool mn88443x_t_is_valid_clk(u32 adckt, u32 if_freq) +{ + if (if_freq == DIRECT_IF_57MHZ) { + if (adckt >= CLK_DIRECT && adckt <= 21000000) + return true; + if (adckt >= 25300000 && adckt <= CLK_MAX) + return true; + } else if (if_freq == DIRECT_IF_44MHZ) { + if (adckt >= 25000000 && adckt <= CLK_MAX) + return true; + } else if (if_freq >= LOW_IF_4MHZ && if_freq < DIRECT_IF_44MHZ) { + if (adckt >= CLK_DIRECT && adckt <= CLK_MAX) + return true; + } + + return false; +} + +static int mn88443x_t_set_freq(struct mn88443x_priv *chip) +{ + struct device *dev = &chip->client_s->dev; + struct regmap *r_t = chip->regmap_t; + s64 adckt, nco, ad_t; + u32 m, v; + + /* Clock buffer (but not supported) or XTAL */ + if (chip->clk_freq >= CLK_LOW && chip->clk_freq < CLK_DIRECT) { + chip->use_clkbuf = true; + regmap_write(r_t, CLKSET1_T, 0x07); + + adckt = 0; + } else { + chip->use_clkbuf = false; + regmap_write(r_t, CLKSET1_T, 0x00); + + adckt = chip->clk_freq; + } + if (!mn88443x_t_is_valid_clk(adckt, chip->if_freq)) { + dev_err(dev, "Invalid clock, CLK:%d, ADCKT:%lld, IF:%d\n", + chip->clk_freq, adckt, chip->if_freq); + return -EINVAL; + } + + /* Direct IF or Low IF */ + if (chip->if_freq == DIRECT_IF_57MHZ || + chip->if_freq == DIRECT_IF_44MHZ) + nco = adckt * 2 - chip->if_freq; + else + nco = -((s64)chip->if_freq); + nco = div_s64(nco << 24, adckt); + ad_t = div_s64(adckt << 22, S_T_FREQ); + + regmap_write(r_t, NCOFREQU_T, nco >> 16); + regmap_write(r_t, NCOFREQM_T, nco >> 8); + regmap_write(r_t, NCOFREQL_T, nco); + regmap_write(r_t, FADU_T, ad_t >> 16); + regmap_write(r_t, FADM_T, ad_t >> 8); + regmap_write(r_t, FADL_T, ad_t); + + /* Level of IF */ + m = ADCSET1_T_REFSEL_MASK; + v = ADCSET1_T_REFSEL_1_5V; + regmap_update_bits(r_t, ADCSET1_T, m, v); + + /* Polarity of AGC */ + v = AGCSET2_T_IFPOLINV_INC | AGCSET2_T_RFPOLINV_INC; + regmap_update_bits(r_t, AGCSET2_T, v, v); + + /* Lower output level of AGC */ + regmap_write(r_t, AGCV3_T, 0x00); + + regmap_write(r_t, MDSET_T, 0xfa); + + return 0; +} + +static void mn88443x_t_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_t = chip->regmap_t; + u32 m, v; + + m = MDSET_T_MDAUTO_MASK | MDSET_T_FFTS_MASK | MDSET_T_GI_MASK; + v = MDSET_T_MDAUTO_AUTO | MDSET_T_FFTS_MODE3 | MDSET_T_GI_1_8; + regmap_update_bits(r_t, MDSET_T, m, v); + + regmap_write(r_t, MDASET_T, 0); +} + +static int mn88443x_t_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_t = chip->regmap_t; + u32 seqrd, st, flg, tmpu, tmpm, tmpl; + u64 tmp; + + /* Sync detection */ + regmap_read(r_t, SSEQRD_T, &seqrd); + st = seqrd & SSEQRD_T_SSEQSTRD_MASK; + + *status = 0; + if (st >= SSEQRD_T_SSEQSTRD_SYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (st >= SSEQRD_T_SSEQSTRD_FRAME_SYNC) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_t, AGCRDU_T, &tmpu); + regmap_read(r_t, AGCRDL_T, &tmpl); + agc = (tmpu << 8) | tmpl; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr; + + regmap_read(r_t, CNRDU_T, &tmpu); + regmap_read(r_t, CNRDL_T, &tmpl); + + if (tmpu || tmpl) { + /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ + /* intlog10(65536) = 80807124, 0.2 * 2^24 = 3355443 */ + tmp = (u64)80807124 - intlog10((tmpu << 8) | tmpl) + + 3355443; + cnr = div_u64(tmp * 10000, 1 << 24); + } else { + cnr = 0; + } + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_t, BERFLG_T, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERFLG_T_BERVRDYA)) { + u32 bit_err, bit_cnt; + + regmap_read(r_t, BERRDU_T, &tmpu); + regmap_read(r_t, BERRDM_T, &tmpm); + regmap_read(r_t, BERRDL_T, &tmpl); + bit_err = (tmpu << 16) | (tmpm << 8) | tmpl; + + regmap_read(r_t, BERLENRDU_T, &tmpu); + regmap_read(r_t, BERLENRDL_T, &tmpl); + bit_cnt = ((tmpu << 8) | tmpl) * 203 * 8; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static int mn88443x_sleep(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; +} + +static int mn88443x_set_frontend(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct regmap *r_s = chip->regmap_s; + struct regmap *r_t = chip->regmap_t; + u8 tssel = 0, intsel = 0; + + if (c->delivery_system == SYS_ISDBS) { + mn88443x_s_wake(chip); + mn88443x_t_sleep(chip); + + tssel = TSSET1_TSASEL_ISDBS; + intsel = TSSET3_INTASEL_S; + } else if (c->delivery_system == SYS_ISDBT) { + mn88443x_s_sleep(chip); + mn88443x_t_wake(chip); + + mn88443x_t_set_freq(chip); + + tssel = TSSET1_TSASEL_ISDBT; + intsel = TSSET3_INTASEL_T; + } + + regmap_update_bits(r_t, TSSET1, + TSSET1_TSASEL_MASK | TSSET1_TSBSEL_MASK, + tssel | TSSET1_TSBSEL_NONE); + regmap_write(r_t, TSSET2, 0); + regmap_update_bits(r_t, TSSET3, + TSSET3_INTASEL_MASK | TSSET3_INTBSEL_MASK, + intsel | TSSET3_INTBSEL_NONE); + + regmap_write(r_t, DOSET1_T, 0x95); + regmap_write(r_s, DOSET1_S, 0x80); + + if (c->delivery_system == SYS_ISDBS) + mn88443x_s_tune(chip, c); + else if (c->delivery_system == SYS_ISDBT) + mn88443x_t_tune(chip, c); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int mn88443x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + s->min_delay_ms = 850; + + if (c->delivery_system == SYS_ISDBS) { + s->max_drift = 30000 * 2 + 1; + s->step_size = 30000; + } else if (c->delivery_system == SYS_ISDBT) { + s->max_drift = 142857 * 2 + 1; + s->step_size = 142857 * 2; + } + + return 0; +} + +static int mn88443x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->delivery_system == SYS_ISDBS) + return mn88443x_s_read_status(chip, c, status); + + if (c->delivery_system == SYS_ISDBT) + return mn88443x_t_read_status(chip, c, status); + + return -EINVAL; +} + +static const struct dvb_frontend_ops mn88443x_ops = { + .delsys = { SYS_ISDBS, SYS_ISDBT }, + .info = { + .name = "Socionext MN88443x", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 2071 * MHz, + .symbol_rate_min = 28860000, + .symbol_rate_max = 28860000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .sleep = mn88443x_sleep, + .set_frontend = mn88443x_set_frontend, + .get_tune_settings = mn88443x_get_tune_settings, + .read_status = mn88443x_read_status, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static int mn88443x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mn88443x_config *conf = client->dev.platform_data; + struct mn88443x_priv *chip; + struct device *dev = &client->dev; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (dev->of_node) + chip->spec = of_device_get_match_data(dev); + else + chip->spec = (struct mn88443x_spec *)id->driver_data; + if (!chip->spec) + return -EINVAL; + + chip->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(chip->mclk) && !conf) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(chip->mclk)); + return PTR_ERR(chip->mclk); + } + + ret = of_property_read_u32(dev->of_node, "if-frequency", + &chip->if_freq); + if (ret && !conf) { + dev_err(dev, "Failed to load IF frequency: %d.\n", ret); + return ret; + } + + chip->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(chip->reset_gpio)) { + dev_err(dev, "Failed to request reset_gpio: %ld\n", + PTR_ERR(chip->reset_gpio)); + return PTR_ERR(chip->reset_gpio); + } + + if (conf) { + chip->mclk = conf->mclk; + chip->if_freq = conf->if_freq; + chip->reset_gpio = conf->reset_gpio; + + *conf->fe = &chip->fe; + } + + chip->client_s = client; + chip->regmap_s = devm_regmap_init_i2c(chip->client_s, ®map_config); + if (IS_ERR(chip->regmap_s)) + return PTR_ERR(chip->regmap_s); + + /* + * Chip has two I2C addresses for each satellite/terrestrial system. + * ISDB-T uses address ISDB-S + 4, so we register a dummy client. + */ + chip->client_t = i2c_new_dummy(client->adapter, client->addr + 4); + if (!chip->client_t) + return -ENODEV; + + chip->regmap_t = devm_regmap_init_i2c(chip->client_t, ®map_config); + if (IS_ERR(chip->regmap_t)) { + ret = PTR_ERR(chip->regmap_t); + goto err_i2c_t; + } + + chip->clk_freq = clk_get_rate(chip->mclk); + + memcpy(&chip->fe.ops, &mn88443x_ops, sizeof(mn88443x_ops)); + chip->fe.demodulator_priv = chip; + i2c_set_clientdata(client, chip); + + mn88443x_cmn_power_on(chip); + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; + +err_i2c_t: + i2c_unregister_device(chip->client_t); + + return ret; +} + +static int mn88443x_remove(struct i2c_client *client) +{ + struct mn88443x_priv *chip = i2c_get_clientdata(client); + + mn88443x_cmn_power_off(chip); + + i2c_unregister_device(chip->client_t); + + return 0; +} + +static const struct mn88443x_spec mn88443x_spec_pri = { + .primary = true, +}; + +static const struct mn88443x_spec mn88443x_spec_sec = { + .primary = false, +}; + +static const struct of_device_id mn88443x_of_match[] = { + { .compatible = "socionext,mn884433", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-0", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-1", .data = &mn88443x_spec_sec, }, + {} +}; +MODULE_DEVICE_TABLE(of, mn88443x_of_match); + +static const struct i2c_device_id mn88443x_i2c_id[] = { + { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); + +static struct i2c_driver mn88443x_driver = { + .driver = { + .name = "mn88443x", + .of_match_table = of_match_ptr(mn88443x_of_match), + }, + .probe = mn88443x_probe, + .remove = mn88443x_remove, + .id_table = mn88443x_i2c_id, +}; + +module_i2c_driver(mn88443x_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki "); +MODULE_DESCRIPTION("Socionext MN88443x series demodulator driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/mn88443x.h b/drivers/media/dvb-frontends/mn88443x.h new file mode 100644 index 000000000000..b19aaf6a1ea3 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. + * + * Copyright (c) 2018 Socionext Inc. + */ + +#ifndef MN88443X_H +#define MN88443X_H + +#include + +/* ISDB-T IF frequency */ +#define DIRECT_IF_57MHZ 57000000 +#define DIRECT_IF_44MHZ 44000000 +#define LOW_IF_4MHZ 4000000 + +struct mn88443x_config { + struct clk *mclk; + u32 if_freq; + struct gpio_desc *reset_gpio; + + /* Everything after that is returned by the driver. */ + struct dvb_frontend **fe; +}; + +#endif /* MN88443X_H */ -- cgit v1.2.3-70-g09d2 From 730129e9ba4dd668452371eba900e6e46f63b778 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Thu, 26 Jul 2018 22:52:49 -0400 Subject: media: dvb-frontends: rtl2832_sdr: Replace GFP_ATOMIC with GFP_KERNEL rtl2832_sdr_submit_urbs(), rtl2832_sdr_alloc_stream_bufs(), and rtl2832_sdr_alloc_urbs() are never called in atomic context. They call usb_submit_urb(), usb_alloc_coherent() and usb_alloc_urb() with GFP_ATOMIC, which is not necessary. GFP_ATOMIC can be replaced with GFP_KERNEL. This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832_sdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c6e78d870ccd..d448d9d4879c 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -301,7 +301,7 @@ static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev) for (i = 0; i < dev->urbs_initialized; i++) { dev_dbg(&pdev->dev, "submit urb=%d\n", i); - ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); + ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL); if (ret) { dev_err(&pdev->dev, "Could not submit urb no. %d - get them all back\n", @@ -345,7 +345,7 @@ static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev) for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, + BULK_BUFFER_SIZE, GFP_KERNEL, &dev->dma_addr[dev->buf_num]); if (!dev->buf_list[dev->buf_num]) { dev_dbg(&pdev->dev, "alloc buf=%d failed\n", @@ -390,7 +390,7 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { dev_dbg(&pdev->dev, "alloc urb=%d\n", i); - dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); if (!dev->urb_list[i]) { for (j = 0; j < i; j++) usb_free_urb(dev->urb_list[j]); -- cgit v1.2.3-70-g09d2 From b9f0f653ea2c214933b16884bd40baf26ff61895 Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Fri, 27 Jul 2018 07:47:59 -0400 Subject: media: vimc: Remove redundant free Commit 4a29b7090749 ("[media] vimc: Subdevices as modules") removes vimc allocation from vimc_probe(), so corresponding deallocation on the error path tries to free static memory. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Acked-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index fe088a953860..9246f265de31 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -328,7 +328,6 @@ static int vimc_probe(struct platform_device *pdev) if (ret) { media_device_cleanup(&vimc->mdev); vimc_rm_subdevs(vimc); - kfree(vimc); return ret; } -- cgit v1.2.3-70-g09d2 From 61e641f36ed81ae473177c085f0bfd83ad3b55ed Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Fri, 27 Jul 2018 07:52:20 -0400 Subject: media: davinci: vpif_display: Mix memory leak on probe error path If vpif_probe() fails on v4l2_device_register() then memory allocated at initialize_vpif() for global vpif_obj.dev[i] become unreleased. The patch adds deallocation of vpif_obj.dev[i] on the error path and removes duplicated check on platform_data presence. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_display.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7be636237acf..0f324055cc9f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1114,6 +1114,14 @@ vpif_init_free_channel_objects: return err; } +static void free_vpif_objs(void) +{ + int i; + + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + static int vpif_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) @@ -1255,11 +1263,6 @@ static __init int vpif_probe(struct platform_device *pdev) return -EINVAL; } - if (!pdev->dev.platform_data) { - dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); - return -EINVAL; - } - vpif_dev = &pdev->dev; err = initialize_vpif(); @@ -1271,7 +1274,7 @@ static __init int vpif_probe(struct platform_device *pdev) err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); if (err) { v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); - return err; + goto vpif_free; } while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { @@ -1314,7 +1317,10 @@ static __init int vpif_probe(struct platform_device *pdev) if (vpif_obj.sd[i]) vpif_obj.sd[i]->grp_id = 1 << i; } - vpif_probe_complete(); + err = vpif_probe_complete(); + if (err) { + goto probe_subdev_out; + } } else { vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; @@ -1334,6 +1340,8 @@ probe_subdev_out: kfree(vpif_obj.sd); vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); +vpif_free: + free_vpif_objs(); return err; } @@ -1355,8 +1363,8 @@ static int vpif_remove(struct platform_device *device) ch = vpif_obj.dev[i]; /* Unregister video device */ video_unregister_device(&ch->video_dev); - kfree(vpif_obj.dev[i]); } + free_vpif_objs(); return 0; } -- cgit v1.2.3-70-g09d2 From 2453e60702e1a4d22720e3b6aac543e1a96fcd6b Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 30 Jul 2018 05:25:07 -0400 Subject: media: usb: em28xx: Replace GFP_ATOMIC with GFP_KERNEL in em28xx_init_usb_xfer() em28xx_init_usb_xfer() is never called in atomic context. It calls usb_submit_urb() with GFP_ATOMIC, which is not necessary. GFP_ATOMIC can be replaced with GFP_KERNEL. This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 45b24776a695..5657f8710ca6 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1053,7 +1053,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, /* submit urbs and enables IRQ */ for (i = 0; i < usb_bufs->num_bufs; i++) { - rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); + rc = usb_submit_urb(usb_bufs->urb[i], GFP_KERNEL); if (rc) { dev_err(&dev->intf->dev, "submit of urb %i failed (error=%i)\n", i, rc); -- cgit v1.2.3-70-g09d2 From 7a3369e8a78538e813e96294b8c7fce9feb76e0f Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 30 Jul 2018 05:24:59 -0400 Subject: media: usb: em28xx: Replace mdelay() with msleep() in em28xx_pre_card_setup() em28xx_pre_card_setup() is never called in atomic context. It calls mdelay() to busily wait, which is not necessary. mdelay() can be replaced with msleep(). This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 98a428769baa..71c829f31d3b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2852,13 +2852,13 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xdc); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc); - mdelay(70); + msleep(70); break; case EM2870_BOARD_TERRATEC_XS_MT2060: /* @@ -2866,11 +2866,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev) * demod work */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); break; case EM2870_BOARD_PINNACLE_PCTV_DVB: /* @@ -2878,11 +2878,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev) * DVB-T demod work */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); break; case EM2820_BOARD_GADMEI_UTV310: case EM2820_BOARD_MSI_VOX_USB_2: -- cgit v1.2.3-70-g09d2 From 2c3449fb95c318920ca8dc645d918d408db219ac Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 30 Jul 2018 05:33:20 -0400 Subject: media: usb: hackrf: Replace GFP_ATOMIC with GFP_KERNEL hackrf_submit_urbs(), hackrf_alloc_stream_bufs() and hackrf_alloc_urbs() are never called in atomic context. They call usb_submit_urb(), usb_alloc_coherent() and usb_alloc_urb() with GFP_ATOMIC, which is not necessary. GFP_ATOMIC can be replaced with GFP_KERNEL. This is found by a static analysis tool named DCNS written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hackrf/hackrf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 6d692fb3e8dd..34085a0b15a1 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -597,7 +597,7 @@ static int hackrf_submit_urbs(struct hackrf_dev *dev) for (i = 0; i < dev->urbs_initialized; i++) { dev_dbg(dev->dev, "submit urb=%d\n", i); - ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); + ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL); if (ret) { dev_err(dev->dev, "Could not submit URB no. %d - get them all back\n", i); @@ -636,7 +636,7 @@ static int hackrf_alloc_stream_bufs(struct hackrf_dev *dev) for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, + BULK_BUFFER_SIZE, GFP_KERNEL, &dev->dma_addr[dev->buf_num]); if (!dev->buf_list[dev->buf_num]) { dev_dbg(dev->dev, "alloc buf=%d failed\n", @@ -689,7 +689,7 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { dev_dbg(dev->dev, "alloc urb=%d\n", i); - dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); if (!dev->urb_list[i]) { for (j = 0; j < i; j++) usb_free_urb(dev->urb_list[j]); -- cgit v1.2.3-70-g09d2 From 3c56b2cccbd1d66ab394ca73f4de48d8c98d923d Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:20 -0400 Subject: media: vsp1: drm: Fix minor grammar error The pixel format is 'unsupported'. Fix the small debug message which incorrectly declares this. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index edb35a5c57ea..a16856856789 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -806,7 +806,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, */ fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat); if (!fmtinfo) { - dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", + dev_dbg(vsp1->dev, "Unsupported pixel format %08x for RPF\n", cfg->pixelformat); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From 3e737e394329564b70ceff8eb0c5f587904aa396 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:21 -0400 Subject: media: vsp1: use kernel __packed for structures The kernel provides a __packed definition to abstract away from the compiler specific attributes tag. Convert all packed structures in VSP1 to use it. The GCC documentation [0] describes this attribute as "the structure or union is placed to minimize the memory required". The Keil compiler documentation at [1] warns that the use of this attribute can cause a performance penalty in the event that the compiler can not deduce the allignment of each field. Careful examination of the object code generated both with and without this attribute shows that these structures are accessed identically and are not affected by any performance penalty. The structures are correctly aligned and padded to match the needs of the hardware already. This patch does not serve to make a decision as to the use of the attribute, but purely to clean up the code to use the kernel defined abstraction as per [2]. [0] https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-packed-type-attribute [1] http://www.keil.com/support/man/docs/armcc/armcc_chr1359124230195.htm [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/compiler-gcc.h?h=v4.16-rc5#n92 Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 10a24bde2299..e4aae334f047 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -25,19 +25,19 @@ struct vsp1_dl_header_list { u32 num_bytes; u32 addr; -} __attribute__((__packed__)); +} __packed; struct vsp1_dl_header { u32 num_lists; struct vsp1_dl_header_list lists[8]; u32 next_header; u32 flags; -} __attribute__((__packed__)); +} __packed; struct vsp1_dl_entry { u32 addr; u32 data; -} __attribute__((__packed__)); +} __packed; /** * struct vsp1_dl_body - Display list body -- cgit v1.2.3-70-g09d2 From 2ca72570b934e820f9a5b645cfb9b5df93e3c139 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:22 -0400 Subject: media: vsp1: Rename dl_child to dl_next Both vsp1_dl_list_commit() and __vsp1_dl_list_put() walk the display list chain referencing the nodes as children, when in reality they are siblings. Update the terminology to 'dl_next' to be consistent with the vsp1_video_pipeline_run() usage. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index e4aae334f047..202bc0c6e484 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -399,7 +399,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) /* This function must be called with the display list manager lock held.*/ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) { - struct vsp1_dl_list *dl_child; + struct vsp1_dl_list *dl_next; if (!dl) return; @@ -409,8 +409,8 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) * hardware operation. */ if (dl->has_chain) { - list_for_each_entry(dl_child, &dl->chain, chain) - __vsp1_dl_list_put(dl_child); + list_for_each_entry(dl_next, &dl->chain, chain) + __vsp1_dl_list_put(dl_next); } dl->has_chain = false; @@ -674,17 +674,17 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) { struct vsp1_dl_manager *dlm = dl->dlm; - struct vsp1_dl_list *dl_child; + struct vsp1_dl_list *dl_next; unsigned long flags; if (dlm->mode == VSP1_DL_MODE_HEADER) { /* Fill the header for the head and chained display lists. */ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); - list_for_each_entry(dl_child, &dl->chain, chain) { - bool last = list_is_last(&dl_child->chain, &dl->chain); + list_for_each_entry(dl_next, &dl->chain, chain) { + bool last = list_is_last(&dl_next->chain, &dl->chain); - vsp1_dl_list_fill_header(dl_child, last); + vsp1_dl_list_fill_header(dl_next, last); } } -- cgit v1.2.3-70-g09d2 From b6ee44d1ea189cf0872b48e4fa56d648dec10fa2 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:23 -0400 Subject: media: vsp1: Remove unused display list structure field The vsp1 reference in the vsp1_dl_body structure is not used. Remove it. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 202bc0c6e484..199843a73df1 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -45,7 +45,6 @@ struct vsp1_dl_entry { * @free: entry in the pool free body list * @refcnt: reference tracking for the body * @pool: pool to which this body belongs - * @vsp1: the VSP1 device * @entries: array of entries * @dma: DMA address of the entries * @size: size of the DMA memory in bytes @@ -59,7 +58,6 @@ struct vsp1_dl_body { refcount_t refcnt; struct vsp1_dl_body_pool *pool; - struct vsp1_device *vsp1; struct vsp1_dl_entry *entries; dma_addr_t dma; -- cgit v1.2.3-70-g09d2 From 8a3a0797dedd39a6ee7a52c3749d31f8104aafa7 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:24 -0400 Subject: media: vsp1: Clean up DLM objects on error If there is an error allocating a display list within a DLM object the existing display lists are not free'd, and neither is the DL body pool. Use the existing vsp1_dlm_destroy() function to clean up on error. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 199843a73df1..64e5b25d1123 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -858,8 +858,10 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, struct vsp1_dl_list *dl; dl = vsp1_dl_list_alloc(dlm); - if (!dl) + if (!dl) { + vsp1_dlm_destroy(dlm); return NULL; + } list_add_tail(&dl->list, &dlm->free); } -- cgit v1.2.3-70-g09d2 From 177fb098b89644b1f1143482a87587a6ed7de578 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:25 -0400 Subject: media: vsp1: Provide VSP1 feature helper macro The VSP1 devices define their specific capabilities through features marked in their device info structure. Various parts of the code read this info structure to infer if the features are available. Wrap this into a more readable vsp1_feature(vsp1, f) macro to ensure that usage is consistent throughout the driver. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 2 ++ drivers/media/platform/vsp1/vsp1_drv.c | 16 ++++++++-------- drivers/media/platform/vsp1/vsp1_wpf.c | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 33f632331474..f0d21cc8e9ab 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -68,6 +68,8 @@ struct vsp1_device_info { bool uapi; }; +#define vsp1_feature(vsp1, f) ((vsp1)->info->features & (f)) + struct vsp1_device { struct device *dev; const struct vsp1_device_info *info; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 5d82f6ee56ea..3367c2ba990d 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -265,7 +265,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Instantiate all the entities. */ - if (vsp1->info->features & VSP1_HAS_BRS) { + if (vsp1_feature(vsp1, VSP1_HAS_BRS)) { vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS); if (IS_ERR(vsp1->brs)) { ret = PTR_ERR(vsp1->brs); @@ -275,7 +275,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_BRU) { + if (vsp1_feature(vsp1, VSP1_HAS_BRU)) { vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU); if (IS_ERR(vsp1->bru)) { ret = PTR_ERR(vsp1->bru); @@ -285,7 +285,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_CLU) { + if (vsp1_feature(vsp1, VSP1_HAS_CLU)) { vsp1->clu = vsp1_clu_create(vsp1); if (IS_ERR(vsp1->clu)) { ret = PTR_ERR(vsp1->clu); @@ -311,7 +311,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->info->features & VSP1_HAS_HGO && vsp1->info->uapi) { + if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) { vsp1->hgo = vsp1_hgo_create(vsp1); if (IS_ERR(vsp1->hgo)) { ret = PTR_ERR(vsp1->hgo); @@ -322,7 +322,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_HGT && vsp1->info->uapi) { + if (vsp1_feature(vsp1, VSP1_HAS_HGT) && vsp1->info->uapi) { vsp1->hgt = vsp1_hgt_create(vsp1); if (IS_ERR(vsp1->hgt)) { ret = PTR_ERR(vsp1->hgt); @@ -353,7 +353,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } } - if (vsp1->info->features & VSP1_HAS_LUT) { + if (vsp1_feature(vsp1, VSP1_HAS_LUT)) { vsp1->lut = vsp1_lut_create(vsp1); if (IS_ERR(vsp1->lut)) { ret = PTR_ERR(vsp1->lut); @@ -387,7 +387,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } } - if (vsp1->info->features & VSP1_HAS_SRU) { + if (vsp1_feature(vsp1, VSP1_HAS_SRU)) { vsp1->sru = vsp1_sru_create(vsp1); if (IS_ERR(vsp1->sru)) { ret = PTR_ERR(vsp1->sru); @@ -537,7 +537,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED); - if (vsp1->info->features & VSP1_HAS_BRS) + if (vsp1_feature(vsp1, VSP1_HAS_BRS)) vsp1_write(vsp1, VI6_DPR_ILV_BRS_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 23c8f706b3f2..c2a1a7f97e26 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -141,13 +141,13 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) if (wpf->entity.index != 0) { /* Only WPF0 supports flipping. */ num_flip_ctrls = 0; - } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { + } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP)) { /* * When horizontal flip is supported the WPF implements three * controls (horizontal flip, vertical flip and rotation). */ num_flip_ctrls = 3; - } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { + } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_VFLIP)) { /* * When only vertical flip is supported the WPF implements a * single control (vertical flip). @@ -276,7 +276,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); - if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && + if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && wpf->entity.index == 0) vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, VI6_WPF_ROT_CTRL_LN16 | -- cgit v1.2.3-70-g09d2 From 08e41f6219c717d16bb22b53010ee7401c27fb11 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:26 -0400 Subject: media: vsp1: Use header display lists for all WPF outputs linked to the DU Header mode display lists are now supported on all WPF outputs. To support extended headers and auto-fld capabilities for interlaced mode handling only header mode display lists can be used. Disable the headerless display list configuration, and remove the dead code. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 108 +++++++++------------------------- 1 file changed, 27 insertions(+), 81 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 64e5b25d1123..e535fee13d54 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -95,7 +95,7 @@ struct vsp1_dl_body_pool { * struct vsp1_dl_list - Display list * @list: entry in the display list manager lists * @dlm: the display list manager - * @header: display list header, NULL for headerless lists + * @header: display list header * @dma: DMA address for the header * @body0: first display list body * @bodies: list of extra display list bodies @@ -119,15 +119,9 @@ struct vsp1_dl_list { bool internal; }; -enum vsp1_dl_mode { - VSP1_DL_MODE_HEADER, - VSP1_DL_MODE_HEADERLESS, -}; - /** * struct vsp1_dl_manager - Display List manager * @index: index of the related WPF - * @mode: display list operation mode (header or headerless) * @singleshot: execute the display list in single-shot mode * @vsp1: the VSP1 device * @lock: protects the free, active, queued, and pending lists @@ -139,7 +133,6 @@ enum vsp1_dl_mode { */ struct vsp1_dl_manager { unsigned int index; - enum vsp1_dl_mode mode; bool singleshot; struct vsp1_device *vsp1; @@ -319,6 +312,7 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; + size_t header_offset; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) @@ -331,16 +325,14 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) dl->body0 = vsp1_dl_body_get(dlm->pool); if (!dl->body0) return NULL; - if (dlm->mode == VSP1_DL_MODE_HEADER) { - size_t header_offset = dl->body0->max_entries - * sizeof(*dl->body0->entries); - dl->header = ((void *)dl->body0->entries) + header_offset; - dl->dma = dl->body0->dma + header_offset; + header_offset = dl->body0->max_entries * sizeof(*dl->body0->entries); - memset(dl->header, 0, sizeof(*dl->header)); - dl->header->lists[0].addr = dl->body0->dma; - } + dl->header = ((void *)dl->body0->entries) + header_offset; + dl->dma = dl->body0->dma + header_offset; + + memset(dl->header, 0, sizeof(*dl->header)); + dl->header->lists[0].addr = dl->body0->dma; return dl; } @@ -472,16 +464,9 @@ struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) * * The reference must be explicitly released by a call to vsp1_dl_body_put() * when the body isn't needed anymore. - * - * Additional bodies are only usable for display lists in header mode. - * Attempting to add a body to a header-less display list will return an error. */ int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { - /* Multi-body lists are only available in header mode. */ - if (dl->dlm->mode != VSP1_DL_MODE_HEADER) - return -EINVAL; - refcount_inc(&dlb->refcnt); list_add_tail(&dlb->list, &dl->bodies); @@ -502,17 +487,10 @@ int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) * Adding a display list to a chain passes ownership of the display list to * the head display list item. The chain is released when the head dl item is * put back with __vsp1_dl_list_put(). - * - * Chained display lists are only usable in header mode. Attempts to add a - * display list to a chain in header-less mode will return an error. */ int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl) { - /* Chained lists are only available in header mode. */ - if (head->dlm->mode != VSP1_DL_MODE_HEADER) - return -EINVAL; - head->has_chain = true; list_add_tail(&dl->chain, &head->chain); return 0; @@ -580,17 +558,10 @@ static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) return false; /* - * Check whether the VSP1 has taken the update. In headerless mode the - * hardware indicates this by clearing the UPD bit in the DL_BODY_SIZE - * register, and in header mode by clearing the UPDHDR bit in the CMD - * register. + * Check whether the VSP1 has taken the update. The hardware indicates + * this by clearing the UPDHDR bit in the CMD register. */ - if (dlm->mode == VSP1_DL_MODE_HEADERLESS) - return !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) - & VI6_DL_BODY_SIZE_UPD); - else - return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) - & VI6_CMD_UPDHDR); + return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) & VI6_CMD_UPDHDR); } static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) @@ -598,26 +569,14 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_device *vsp1 = dlm->vsp1; - if (dlm->mode == VSP1_DL_MODE_HEADERLESS) { - /* - * In headerless mode, program the hardware directly with the - * display list body address and size and set the UPD bit. The - * bit will be cleared by the hardware when the display list - * processing starts. - */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma); - vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->body0->num_entries * sizeof(*dl->header->lists))); - } else { - /* - * In header mode, program the display list header address. If - * the hardware is idle (single-shot mode or first frame in - * continuous mode) it will then be started independently. If - * the hardware is operating, the VI6_DL_HDR_REF_ADDR register - * will be updated with the display list address. - */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); - } + /* + * Program the display list header address. If the hardware is idle + * (single-shot mode or first frame in continuous mode) it will then be + * started independently. If the hardware is operating, the + * VI6_DL_HDR_REF_ADDR register will be updated with the display list + * address. + */ + vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); } static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) @@ -675,15 +634,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) struct vsp1_dl_list *dl_next; unsigned long flags; - if (dlm->mode == VSP1_DL_MODE_HEADER) { - /* Fill the header for the head and chained display lists. */ - vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); + /* Fill the header for the head and chained display lists. */ + vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); - list_for_each_entry(dl_next, &dl->chain, chain) { - bool last = list_is_last(&dl_next->chain, &dl->chain); + list_for_each_entry(dl_next, &dl->chain, chain) { + bool last = list_is_last(&dl_next->chain, &dl->chain); - vsp1_dl_list_fill_header(dl_next, last); - } + vsp1_dl_list_fill_header(dl_next, last); } dl->internal = internal; @@ -712,7 +669,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) * has completed at frame end. If the flag is not returned display list * completion has been delayed by one frame because the display list commit * raced with the frame end interrupt. The function always returns with the flag - * set in header mode as display list processing is then not continuous and + * set in single-shot mode as display list processing is then not continuous and * races never occur. * * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list @@ -784,13 +741,6 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 | VI6_DL_CTRL_DLE; - /* - * The DRM pipeline operates with display lists in Continuous Frame - * Mode, all other pipelines use manual start. - */ - if (vsp1->drm) - ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; - vsp1_write(vsp1, VI6_DL_CTRL, ctrl); vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); } @@ -830,8 +780,6 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, return NULL; dlm->index = index; - dlm->mode = index == 0 && !vsp1->info->uapi - ? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER; dlm->singleshot = vsp1->info->uapi; dlm->vsp1 = vsp1; @@ -840,14 +788,12 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, /* * Initialize the display list body and allocate DMA memory for the body - * and the optional header. Both are allocated together to avoid memory + * and the header. Both are allocated together to avoid memory * fragmentation, with the header located right after the body in * memory. An extra body is allocated on top of the prealloc to account * for the cached body used by the vsp1_pipeline object. */ - header_size = dlm->mode == VSP1_DL_MODE_HEADER - ? ALIGN(sizeof(struct vsp1_dl_header), 8) - : 0; + header_size = ALIGN(sizeof(struct vsp1_dl_header), 8); dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, VSP1_DL_NUM_ENTRIES, header_size); -- cgit v1.2.3-70-g09d2 From 7d630c4924d82c4679e2df4bf55c2fd94e668e91 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:27 -0400 Subject: media: vsp1: Add support for extended display list headers Extended display list headers allow pre and post command lists to be executed by the VSP pipeline. This provides the base support for features such as AUTO_FLD (for interlaced support) and AUTO_DISP (for supporting continuous camera preview pipelines. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 1 + drivers/media/platform/vsp1/vsp1_dl.c | 105 +++++++++++++++++++++++++++++++- drivers/media/platform/vsp1/vsp1_dl.h | 25 ++++++++ drivers/media/platform/vsp1/vsp1_drv.c | 4 +- drivers/media/platform/vsp1/vsp1_regs.h | 2 +- 5 files changed, 133 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index f0d21cc8e9ab..56c62122a81a 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -53,6 +53,7 @@ struct vsp1_uif; #define VSP1_HAS_HGO (1 << 7) #define VSP1_HAS_HGT (1 << 8) #define VSP1_HAS_BRS (1 << 9) +#define VSP1_HAS_EXT_DL (1 << 10) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index e535fee13d54..309ac4a38586 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -22,6 +22,9 @@ #define VSP1_DLH_INT_ENABLE (1 << 1) #define VSP1_DLH_AUTO_START (1 << 0) +#define VSP1_DLH_EXT_PRE_CMD_EXEC (1 << 9) +#define VSP1_DLH_EXT_POST_CMD_EXEC (1 << 8) + struct vsp1_dl_header_list { u32 num_bytes; u32 addr; @@ -34,11 +37,58 @@ struct vsp1_dl_header { u32 flags; } __packed; +/** + * struct vsp1_dl_ext_header - Extended display list header + * @padding: padding zero bytes for alignment + * @pre_ext_dl_num_cmd: number of pre-extended command bodies to parse + * @flags: enables or disables execution of the pre and post command + * @pre_ext_dl_plist: start address of pre-extended display list bodies + * @post_ext_dl_num_cmd: number of post-extended command bodies to parse + * @post_ext_dl_plist: start address of post-extended display list bodies + */ +struct vsp1_dl_ext_header { + u32 padding; + + /* + * The datasheet represents flags as stored before pre_ext_dl_num_cmd, + * expecting 32-bit accesses. The flags are appropriate to the whole + * header, not just the pre_ext command, and thus warrant being + * separated out. Due to byte ordering, and representing as 16 bit + * values here, the flags must be positioned after the + * pre_ext_dl_num_cmd. + */ + u16 pre_ext_dl_num_cmd; + u16 flags; + u32 pre_ext_dl_plist; + + u32 post_ext_dl_num_cmd; + u32 post_ext_dl_plist; +} __packed; + +struct vsp1_dl_header_extended { + struct vsp1_dl_header header; + struct vsp1_dl_ext_header ext; +} __packed; + struct vsp1_dl_entry { u32 addr; u32 data; } __packed; +/** + * struct vsp1_pre_ext_dl_body - Pre Extended Display List Body + * @opcode: Extended display list command operation code + * @flags: Pre-extended command flags. These are specific to each command + * @address_set: Source address set pointer. Must have 16-byte alignment + * @reserved: Zero bits for alignment. + */ +struct vsp1_pre_ext_dl_body { + u32 opcode; + u32 flags; + u32 address_set; + u32 reserved; +} __packed; + /** * struct vsp1_dl_body - Display list body * @list: entry in the display list list of bodies @@ -96,9 +146,12 @@ struct vsp1_dl_body_pool { * @list: entry in the display list manager lists * @dlm: the display list manager * @header: display list header + * @extension: extended display list header. NULL for normal lists * @dma: DMA address for the header * @body0: first display list body * @bodies: list of extra display list bodies + * @pre_cmd: pre command to be issued through extended dl header + * @post_cmd: post command to be issued through extended dl header * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain * @internal: whether the display list is used for internal purpose @@ -108,11 +161,15 @@ struct vsp1_dl_list { struct vsp1_dl_manager *dlm; struct vsp1_dl_header *header; + struct vsp1_dl_ext_header *extension; dma_addr_t dma; struct vsp1_dl_body *body0; struct list_head bodies; + struct vsp1_dl_ext_cmd *pre_cmd; + struct vsp1_dl_ext_cmd *post_cmd; + bool has_chain; struct list_head chain; @@ -496,6 +553,14 @@ int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, return 0; } +static void vsp1_dl_ext_cmd_fill_header(struct vsp1_dl_ext_cmd *cmd) +{ + cmd->cmds[0].opcode = cmd->opcode; + cmd->cmds[0].flags = cmd->flags; + cmd->cmds[0].address_set = cmd->data_dma; + cmd->cmds[0].reserved = 0; +} + static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) { struct vsp1_dl_manager *dlm = dl->dlm; @@ -548,6 +613,27 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) */ dl->header->flags = VSP1_DLH_INT_ENABLE; } + + if (!dl->extension) + return; + + dl->extension->flags = 0; + + if (dl->pre_cmd) { + dl->extension->pre_ext_dl_plist = dl->pre_cmd->cmd_dma; + dl->extension->pre_ext_dl_num_cmd = dl->pre_cmd->num_cmds; + dl->extension->flags |= VSP1_DLH_EXT_PRE_CMD_EXEC; + + vsp1_dl_ext_cmd_fill_header(dl->pre_cmd); + } + + if (dl->post_cmd) { + dl->extension->post_ext_dl_plist = dl->post_cmd->cmd_dma; + dl->extension->post_ext_dl_num_cmd = dl->post_cmd->num_cmds; + dl->extension->flags |= VSP1_DLH_EXT_POST_CMD_EXEC; + + vsp1_dl_ext_cmd_fill_header(dl->post_cmd); + } } static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) @@ -737,9 +823,17 @@ done: /* Hardware Setup */ void vsp1_dlm_setup(struct vsp1_device *vsp1) { + unsigned int i; u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 | VI6_DL_CTRL_DLE; + u32 ext_dl = (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT) + | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT; + + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { + for (i = 0; i < vsp1->info->wpf_count; ++i) + vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl); + } vsp1_write(vsp1, VI6_DL_CTRL, ctrl); vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); @@ -793,7 +887,11 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, * memory. An extra body is allocated on top of the prealloc to account * for the cached body used by the vsp1_pipeline object. */ - header_size = ALIGN(sizeof(struct vsp1_dl_header), 8); + header_size = vsp1_feature(vsp1, VSP1_HAS_EXT_DL) ? + sizeof(struct vsp1_dl_header_extended) : + sizeof(struct vsp1_dl_header); + + header_size = ALIGN(header_size, 8); dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, VSP1_DL_NUM_ENTRIES, header_size); @@ -809,6 +907,11 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, return NULL; } + /* The extended header immediately follows the header. */ + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) + dl->extension = (void *)dl->header + + sizeof(*dl->header); + list_add_tail(&dl->list, &dlm->free); } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 7dba0469c92e..afefd5bfa136 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -20,6 +20,31 @@ struct vsp1_dl_manager; #define VSP1_DL_FRAME_END_COMPLETED BIT(0) #define VSP1_DL_FRAME_END_INTERNAL BIT(1) +/** + * struct vsp1_dl_ext_cmd - Extended Display command + * @free: entry in the pool of free commands list + * @opcode: command type opcode + * @flags: flags used by the command + * @cmds: array of command bodies for this extended cmd + * @num_cmds: quantity of commands in @cmds array + * @cmd_dma: DMA address of the command body + * @data: memory allocation for command-specific data + * @data_dma: DMA address for command-specific data + */ +struct vsp1_dl_ext_cmd { + struct list_head free; + + u8 opcode; + u32 flags; + + struct vsp1_pre_ext_dl_body *cmds; + unsigned int num_cmds; + dma_addr_t cmd_dma; + + void *data; + dma_addr_t data_dma; +}; + void vsp1_dlm_setup(struct vsp1_device *vsp1); struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 3367c2ba990d..b6619c9c18bb 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -754,7 +754,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, .model = "VSP2-D", .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP | VSP1_HAS_EXT_DL, .lif_count = 1, .rpf_count = 5, .uif_count = 1, @@ -774,7 +774,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPDL_GEN3, .model = "VSP2-DL", .gen = 3, - .features = VSP1_HAS_BRS | VSP1_HAS_BRU, + .features = VSP1_HAS_BRS | VSP1_HAS_BRU | VSP1_HAS_EXT_DL, .lif_count = 2, .rpf_count = 5, .uif_count = 2, diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 0d249ff9f564..5ea9f9070cf3 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -72,7 +72,7 @@ #define VI6_DL_SWAP_WDS (1 << 1) #define VI6_DL_SWAP_BTS (1 << 0) -#define VI6_DL_EXT_CTRL 0x011c +#define VI6_DL_EXT_CTRL(n) (0x011c + (n) * 36) #define VI6_DL_EXT_CTRL_NWE (1 << 16) #define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8) #define VI6_DL_EXT_CTRL_POLINT_SHIFT 8 -- cgit v1.2.3-70-g09d2 From f3b98e3c4d2e16bb7c99fc75d652559bd591070f Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:28 -0400 Subject: media: vsp1: Provide support for extended command pools VSPD and VSP-DL devices can provide extended display lists supporting extended command display list objects. These extended commands require their own dma memory areas for a header and body specific to the command type. Implement a command pool to allocate all necessary memory in a single DMA allocation to reduce pressure on the TLB, and provide convenient re-usable command objects for the entities to utilise. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 193 ++++++++++++++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_dl.h | 3 + 2 files changed, 196 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 309ac4a38586..6021c43c53b4 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -141,6 +141,30 @@ struct vsp1_dl_body_pool { struct vsp1_device *vsp1; }; +/** + * struct vsp1_cmd_pool - Display List commands pool + * @dma: DMA address of the entries + * @size: size of the full DMA memory pool in bytes + * @mem: CPU memory pointer for the pool + * @cmds: Array of command structures for the pool + * @free: Free pool entries + * @lock: Protects the free list + * @vsp1: the VSP1 device + */ +struct vsp1_dl_cmd_pool { + /* DMA allocation */ + dma_addr_t dma; + size_t size; + void *mem; + + struct vsp1_dl_ext_cmd *cmds; + struct list_head free; + + spinlock_t lock; + + struct vsp1_device *vsp1; +}; + /** * struct vsp1_dl_list - Display list * @list: entry in the display list manager lists @@ -187,6 +211,7 @@ struct vsp1_dl_list { * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware * @pool: body pool for the display list bodies + * @autofld_cmds: command pool to support auto-fld interlaced mode */ struct vsp1_dl_manager { unsigned int index; @@ -200,6 +225,7 @@ struct vsp1_dl_manager { struct vsp1_dl_list *pending; struct vsp1_dl_body_pool *pool; + struct vsp1_dl_cmd_pool *cmdpool; }; /* ----------------------------------------------------------------------------- @@ -363,6 +389,157 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Display List Extended Command Management + */ + +enum vsp1_extcmd_type { + VSP1_EXTCMD_AUTODISP, + VSP1_EXTCMD_AUTOFLD, +}; + +struct vsp1_extended_command_info { + u16 opcode; + size_t body_size; +}; + +static const struct vsp1_extended_command_info vsp1_extended_commands[] = { + [VSP1_EXTCMD_AUTODISP] = { 0x02, 96 }, + [VSP1_EXTCMD_AUTOFLD] = { 0x03, 160 }, +}; + +/** + * vsp1_dl_cmd_pool_create - Create a pool of commands from a single allocation + * @vsp1: The VSP1 device + * @type: The command pool type + * @num_cmds: The number of commands to allocate + * + * Allocate a pool of commands each with enough memory to contain the private + * data of each command. The allocation sizes are dependent upon the command + * type. + * + * Return a pointer to the pool on success or NULL if memory can't be allocated. + */ +static struct vsp1_dl_cmd_pool * +vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, + unsigned int num_cmds) +{ + struct vsp1_dl_cmd_pool *pool; + unsigned int i; + size_t cmd_size; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; + + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->free); + + pool->cmds = kcalloc(num_cmds, sizeof(*pool->cmds), GFP_KERNEL); + if (!pool->cmds) { + kfree(pool); + return NULL; + } + + cmd_size = sizeof(struct vsp1_pre_ext_dl_body) + + vsp1_extended_commands[type].body_size; + cmd_size = ALIGN(cmd_size, 16); + + pool->size = cmd_size * num_cmds; + pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, + GFP_KERNEL); + if (!pool->mem) { + kfree(pool->cmds); + kfree(pool); + return NULL; + } + + for (i = 0; i < num_cmds; ++i) { + struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i]; + size_t cmd_offset = i * cmd_size; + /* data_offset must be 16 byte aligned for DMA. */ + size_t data_offset = sizeof(struct vsp1_pre_ext_dl_body) + + cmd_offset; + + cmd->pool = pool; + cmd->opcode = vsp1_extended_commands[type].opcode; + + /* + * TODO: Auto-disp can utilise more than one extended body + * command per cmd. + */ + cmd->num_cmds = 1; + cmd->cmds = pool->mem + cmd_offset; + cmd->cmd_dma = pool->dma + cmd_offset; + + cmd->data = pool->mem + data_offset; + cmd->data_dma = pool->dma + data_offset; + + list_add_tail(&cmd->free, &pool->free); + } + + return pool; +} + +static +struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) +{ + struct vsp1_dl_ext_cmd *cmd = NULL; + unsigned long flags; + + spin_lock_irqsave(&pool->lock, flags); + + if (!list_empty(&pool->free)) { + cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, + free); + list_del(&cmd->free); + } + + spin_unlock_irqrestore(&pool->lock, flags); + + return cmd; +} + +static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd) +{ + unsigned long flags; + + if (!cmd) + return; + + /* Reset flags, these mark data usage. */ + cmd->flags = 0; + + spin_lock_irqsave(&cmd->pool->lock, flags); + list_add_tail(&cmd->free, &cmd->pool->free); + spin_unlock_irqrestore(&cmd->pool->lock, flags); +} + +static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool) +{ + if (!pool) + return; + + if (pool->mem) + dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, + pool->dma); + + kfree(pool->cmds); + kfree(pool); +} + +struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl) +{ + struct vsp1_dl_manager *dlm = dl->dlm; + + if (dl->pre_cmd) + return dl->pre_cmd; + + dl->pre_cmd = vsp1_dl_ext_cmd_get(dlm->cmdpool); + + return dl->pre_cmd; +} + +/* ---------------------------------------------------------------------------- * Display List Transaction Management */ @@ -464,6 +641,12 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) vsp1_dl_list_bodies_put(dl); + vsp1_dl_ext_cmd_put(dl->pre_cmd); + vsp1_dl_ext_cmd_put(dl->post_cmd); + + dl->pre_cmd = NULL; + dl->post_cmd = NULL; + /* * body0 is reused as as an optimisation as presently every display list * has at least one body, thus we reinitialise the entries list. @@ -915,6 +1098,15 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, list_add_tail(&dl->list, &dlm->free); } + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { + dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1, + VSP1_EXTCMD_AUTOFLD, prealloc); + if (!dlm->cmdpool) { + vsp1_dlm_destroy(dlm); + return NULL; + } + } + return dlm; } @@ -931,4 +1123,5 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) } vsp1_dl_body_pool_destroy(dlm->pool); + vsp1_dl_ext_cmd_pool_destroy(dlm->cmdpool); } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index afefd5bfa136..125750dc8b5c 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -22,6 +22,7 @@ struct vsp1_dl_manager; /** * struct vsp1_dl_ext_cmd - Extended Display command + * @pool: pool to which this command belongs * @free: entry in the pool of free commands list * @opcode: command type opcode * @flags: flags used by the command @@ -32,6 +33,7 @@ struct vsp1_dl_manager; * @data_dma: DMA address for command-specific data */ struct vsp1_dl_ext_cmd { + struct vsp1_dl_cmd_pool *pool; struct list_head free; u8 opcode; @@ -58,6 +60,7 @@ struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm); struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); void vsp1_dl_list_put(struct vsp1_dl_list *dl); struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl); +struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl); void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal); struct vsp1_dl_body_pool * -- cgit v1.2.3-70-g09d2 From e90561d40f830f1266d9531ae95eae8252dd8fa1 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Aug 2018 07:37:29 -0400 Subject: media: vsp1: Support Interlaced display pipelines Calculate the top and bottom fields for the interlaced frames and utilise the extended display list command feature to implement the auto-field operations. This allows the DU to update the VSP2 registers dynamically based upon the currently processing field. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 10 +++++ drivers/media/platform/vsp1/vsp1_drm.c | 6 ++- drivers/media/platform/vsp1/vsp1_pipe.h | 2 + drivers/media/platform/vsp1/vsp1_regs.h | 3 ++ drivers/media/platform/vsp1/vsp1_rpf.c | 72 +++++++++++++++++++++++++++++++-- include/media/vsp1.h | 2 + 6 files changed, 90 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 6021c43c53b4..9255b5ee2cb8 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -947,6 +947,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) */ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { + struct vsp1_device *vsp1 = dlm->vsp1; + u32 status = vsp1_read(vsp1, VI6_STATUS); unsigned int flags = 0; spin_lock(&dlm->lock); @@ -971,6 +973,14 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) if (vsp1_dl_list_hw_update_pending(dlm)) goto done; + /* + * Progressive streams report only TOP fields. If we have a BOTTOM + * field, we are interlaced, and expect the frame to complete on the + * next frame end interrupt. + */ + if (status & VI6_STATUS_FLD_STD(dlm->index)) + goto done; + /* * The device starts processing the queued display list right after the * frame end interrupt. The display list thus becomes active. diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a16856856789..87dc3ddbe2d3 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -670,9 +670,11 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe->width = cfg->width; drm_pipe->height = cfg->height; + pipe->interlaced = cfg->interlaced; - dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n", - __func__, pipe_index, cfg->width, cfg->height); + dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u%s\n", + __func__, pipe_index, cfg->width, cfg->height, + pipe->interlaced ? "i" : ""); mutex_lock(&vsp1->drm->lock); diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 743d8f0db45c..ae646c9ef337 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -104,6 +104,7 @@ struct vsp1_partition { * @entities: list of entities in the pipeline * @stream_config: cached stream configuration for video pipelines * @configured: when false the @stream_config shall be written to the hardware + * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline @@ -142,6 +143,7 @@ struct vsp1_pipeline { struct vsp1_dl_body *stream_config; bool configured; + bool interlaced; unsigned int partitions; struct vsp1_partition *partition; diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 5ea9f9070cf3..3738ff2f7b85 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -28,6 +28,7 @@ #define VI6_SRESET_SRTS(n) (1 << (n)) #define VI6_STATUS 0x0038 +#define VI6_STATUS_FLD_STD(n) (1 << ((n) + 28)) #define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8)) #define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12) @@ -80,6 +81,8 @@ #define VI6_DL_EXT_CTRL_EXPRI (1 << 4) #define VI6_DL_EXT_CTRL_EXT (1 << 0) +#define VI6_DL_EXT_AUTOFLD_INT BIT(0) + #define VI6_DL_BODY_SIZE 0x0120 #define VI6_DL_BODY_SIZE_UPD (1 << 24) #define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 69e5fe6e6b50..f8005b60b9d2 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -20,6 +20,18 @@ #define RPF_MAX_WIDTH 8190 #define RPF_MAX_HEIGHT 8190 +/* Pre extended display list command data structure. */ +struct vsp1_extcmd_auto_fld_body { + u32 top_y0; + u32 bottom_y0; + u32 top_c0; + u32 bottom_c0; + u32 top_c1; + u32 bottom_c1; + u32 reserved0; + u32 reserved1; +} __packed; + /* ----------------------------------------------------------------------------- * Device Access */ @@ -64,6 +76,14 @@ static void rpf_configure_stream(struct vsp1_entity *entity, pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + /* + * pstride has both STRIDE_Y and STRIDE_C, but multiplying the whole + * of pstride by 2 is conveniently OK here as we are multiplying both + * values. + */ + if (pipe->interlaced) + pstride *= 2; + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ @@ -100,6 +120,9 @@ static void rpf_configure_stream(struct vsp1_entity *entity, top = compose->top; } + if (pipe->interlaced) + top /= 2; + vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); @@ -169,6 +192,36 @@ static void rpf_configure_stream(struct vsp1_entity *entity, } +static void vsp1_rpf_configure_autofld(struct vsp1_rwpf *rpf, + struct vsp1_dl_list *dl) +{ + const struct v4l2_pix_format_mplane *format = &rpf->format; + struct vsp1_dl_ext_cmd *cmd; + struct vsp1_extcmd_auto_fld_body *auto_fld; + u32 offset_y, offset_c; + + cmd = vsp1_dl_get_pre_cmd(dl); + if (WARN_ONCE(!cmd, "Failed to obtain an autofld cmd")) + return; + + /* Re-index our auto_fld to match the current RPF. */ + auto_fld = cmd->data; + auto_fld = &auto_fld[rpf->entity.index]; + + auto_fld->top_y0 = rpf->mem.addr[0]; + auto_fld->top_c0 = rpf->mem.addr[1]; + auto_fld->top_c1 = rpf->mem.addr[2]; + + offset_y = format->plane_fmt[0].bytesperline; + offset_c = format->plane_fmt[1].bytesperline; + + auto_fld->bottom_y0 = rpf->mem.addr[0] + offset_y; + auto_fld->bottom_c0 = rpf->mem.addr[1] + offset_c; + auto_fld->bottom_c1 = rpf->mem.addr[2] + offset_c; + + cmd->flags |= VI6_DL_EXT_AUTOFLD_INT | BIT(16 + rpf->entity.index); +} + static void rpf_configure_frame(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, @@ -221,6 +274,11 @@ static void rpf_configure_partition(struct vsp1_entity *entity, crop.left += pipe->partition->rpf.left; } + if (pipe->interlaced) { + crop.height = round_down(crop.height / 2, fmtinfo->vsub); + crop.top = round_down(crop.top / 2, fmtinfo->vsub); + } + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE, (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); @@ -249,9 +307,17 @@ static void rpf_configure_partition(struct vsp1_entity *entity, fmtinfo->swap_uv) swap(mem.addr[1], mem.addr[2]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); + /* + * Interlaced pipelines will use the extended pre-cmd to process + * SRCM_ADDR_{Y,C0,C1} + */ + if (pipe->interlaced) { + vsp1_rpf_configure_autofld(rpf, dl); + } else { + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); + } } static void rpf_partition(struct vsp1_entity *entity, diff --git a/include/media/vsp1.h b/include/media/vsp1.h index 678c24de1ac6..3093b9cb9067 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -25,6 +25,7 @@ int vsp1_du_init(struct device *dev); * struct vsp1_du_lif_config - VSP LIF configuration * @width: output frame width * @height: output frame height + * @interlaced: true for interlaced pipelines * @callback: frame completion callback function (optional). When a callback * is provided, the VSP driver guarantees that it will be called once * and only once for each vsp1_du_atomic_flush() call. @@ -33,6 +34,7 @@ int vsp1_du_init(struct device *dev); struct vsp1_du_lif_config { unsigned int width; unsigned int height; + bool interlaced; void (*callback)(void *data, bool completed, u32 crc); void *callback_data; -- cgit v1.2.3-70-g09d2 From 4679b79f217550f2f4c83101eb8149c5e552a93a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:34:42 -0400 Subject: media: soc_camera_platform: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera_platform.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index ce00e90d4e3c..6745a6e3f464 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic Platform Camera Driver * * Copyright (C) 2008 Magnus Damm * Based on mt9m001 driver, * Copyright (C) 2008, 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 -- cgit v1.2.3-70-g09d2 From aa7b827878f06876c32b050d1737c3a1a3816af1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:35:06 -0400 Subject: media: rcar-vin: convert to SPDX identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kconfig and Makefile doesn't have license line, thus, these are GPL-2.0 as default. All ohter files are GPL-2.0+ as original license. Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Acked-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/Kconfig | 1 + drivers/media/platform/rcar-vin/Makefile | 1 + drivers/media/platform/rcar-vin/rcar-core.c | 8 ++------ drivers/media/platform/rcar-vin/rcar-dma.c | 6 +----- drivers/media/platform/rcar-vin/rcar-v4l2.c | 6 +----- drivers/media/platform/rcar-vin/rcar-vin.h | 6 +----- 6 files changed, 7 insertions(+), 21 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index baf4eafff6e3..e3eb8fee2536 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config VIDEO_RCAR_CSI2 tristate "R-Car MIPI CSI-2 Receiver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile index 5ab803d3e7c1..00d809f5d2c1 100644 --- a/drivers/media/platform/rcar-vin/Makefile +++ b/drivers/media/platform/rcar-vin/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 8843367cd339..ce09799976ef 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include @@ -1275,4 +1271,4 @@ module_platform_driver(rcar_vin_driver); MODULE_AUTHOR("Niklas Söderlund "); MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 9146f63430c2..92323310f735 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 87a718b1c2e6..5a54779cfc27 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index df2a47e4be31..0b13b34d03e3 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef __RCAR_VIN__ -- cgit v1.2.3-70-g09d2 From 58c708f64aee6a0adf4e7ca00589c0bf1dd69fe6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:35:23 -0400 Subject: media: rcar-fcp: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-fcp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index 2988031d285d..b47af8eb145a 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * rcar-fcp.c -- R-Car Frame Compression Processor Driver * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include -- cgit v1.2.3-70-g09d2 From adeb6970568256e4208b1b131f710f544c58d5c8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:35:49 -0400 Subject: media: rcar_drif: convert to SPDX identifiers As original license mentioned, it is GPL-2.0+ in SPDX. Then, MODULE_LICENSE() should be "GPL" instead of "GPL v2". See ${LINUX}/include/linux/module.h "GPL" [GNU Public License v2 or later] "GPL v2" [GNU Public License v2] Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Acked-by: Ramesh Shanmugasundaram Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_drif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index dc7e280c91b4..81413ab52475 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * R-Car Gen3 Digital Radio Interface (DRIF) driver * * Copyright (C) 2017 Renesas Electronics Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -1499,5 +1495,5 @@ module_platform_driver(rcar_drif_driver); MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver"); MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ramesh Shanmugasundaram "); -- cgit v1.2.3-70-g09d2 From 7c1d62e455ab934ab3fdf86e03bc2a1fa550aa23 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:36:04 -0400 Subject: media: rcar_fdp1: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Acked-by: Kieran Bingham Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_fdp1.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index b13dec3081e5..2a15b7cca338 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Renesas R-Car Fine Display Processor * @@ -8,11 +9,6 @@ * * This code is developed and inspired from the vim2m, rcar_jpu, * m2m-deinterlace, and vsp1 drivers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the - * License, or (at your option) any later version */ #include -- cgit v1.2.3-70-g09d2 From 88352b163c839277be367ddcf13ef5971ce9df2a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:36:21 -0400 Subject: media: rcar_jpu: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_jpu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index a808258a4180..e5c882423f57 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Author: Mikhail Ulyanov * Copyright (C) 2014-2015 Cogent Embedded, Inc. @@ -11,10 +12,6 @@ * 1) Rotation * 2) Cropping * 3) V4L2_CID_JPEG_ACTIVE_MARKER - * - * 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 -- cgit v1.2.3-70-g09d2 From d81469d2b77553b281b9b59eadd995309728f506 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:36:36 -0400 Subject: media: sh_veu: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 1a0cde017fdf..1d274c64de09 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * sh-mobile VEU mem2mem driver * * Copyright (C) 2012 Renesas Electronics Corporation * Author: Guennadi Liakhovetski, * Copyright (C) 2008 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License as - * published by the Free Software Foundation */ #include -- cgit v1.2.3-70-g09d2 From 7b381978c8563672e19d90db00119d74ed2221b3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:36:55 -0400 Subject: media: sh_vou: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 4dccf29e9d78..6135e13e24d4 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SuperH Video Output Unit (VOU) driver * * Copyright (C) 2010, 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 -- cgit v1.2.3-70-g09d2 From 12f336c88090fb8004736fd4329184326a49673b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 25 Jul 2018 22:37:11 -0400 Subject: media: sh_mobile_ceu: convert to SPDX identifiers Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 9897213f2618..0a2c0daaffef 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * V4L2 Driver for SuperH Mobile CEU interface * @@ -7,11 +8,6 @@ * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include -- cgit v1.2.3-70-g09d2 From 16200248f48dded48f7c79a0d20d18b8c6b02605 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 3 Aug 2018 10:36:01 -0400 Subject: media: dvb-usb: fix spelling mistake: "completition" -> "completion" Trivial fix to spelling mistake in debug and error messages Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/usb-urb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c index 5e05963f4220..9771f0954c69 100644 --- a/drivers/media/usb/dvb-usb/usb-urb.c +++ b/drivers/media/usb/dvb-usb/usb-urb.c @@ -33,7 +33,7 @@ static void usb_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - deb_ts("urb completition error %d.\n", urb->status); + deb_ts("urb completion error %d.\n", urb->status); break; } @@ -57,7 +57,7 @@ static void usb_urb_complete(struct urb *urb) stream->complete(stream, b, urb->actual_length); break; default: - err("unknown endpoint type in completition handler."); + err("unknown endpoint type in completion handler."); return; } usb_submit_urb(urb,GFP_ATOMIC); -- cgit v1.2.3-70-g09d2 From 3c8496e5d0595cec6e463b35b7ad87eefabc531f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 3 Aug 2018 10:36:00 -0400 Subject: media: dvb-usb-v2: fix spelling mistake: "completition" -> "completion" Trivial fix to spelling mistake in dev_dbg and dev_err messages Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/usb_urb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c index b0499f95ec45..024c751eb165 100644 --- a/drivers/media/usb/dvb-usb-v2/usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -40,7 +40,7 @@ static void usb_urb_complete(struct urb *urb) return; default: /* error */ dev_dbg_ratelimited(&stream->udev->dev, - "%s: urb completition failed=%d\n", + "%s: urb completion failed=%d\n", __func__, urb->status); break; } @@ -69,7 +69,7 @@ static void usb_urb_complete(struct urb *urb) break; default: dev_err(&stream->udev->dev, - "%s: unknown endpoint type in completition handler\n", + "%s: unknown endpoint type in completion handler\n", KBUILD_MODNAME); return; } -- cgit v1.2.3-70-g09d2 From 854bb4ec126320046afd2e299c8dd070946d2410 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 3 Aug 2018 10:35:59 -0400 Subject: media: cx231xx: fix spelling mistake: "completition" -> "completion" Trivial fix to spelling mistake in dev_dbg debug messages Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-audio.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 0025187c28e3..32ee7b3f21c9 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -112,7 +112,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_dbg(dev->dev, "urb completition error %d.\n", + dev_dbg(dev->dev, "urb completion error %d.\n", urb->status); break; } @@ -203,7 +203,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_dbg(dev->dev, "urb completition error %d.\n", + dev_dbg(dev->dev, "urb completion error %d.\n", urb->status); break; } diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index 920417baf893..10b2eb7338ad 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -317,7 +317,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) return; default: /* error */ dev_err(dev->dev, - "urb completition error %d.\n", urb->status); + "urb completion error %d.\n", urb->status); break; } -- cgit v1.2.3-70-g09d2 From 1e506464a4d5c5d7482fda1f745b9ec79e44ad4f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 3 Aug 2018 10:35:58 -0400 Subject: media: au0828: fix spelling mistake: "completition" -> "completion" Trivial fix to spelling mistake in au0828_isocdbg debug message Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 70e187971590..62b45062b1e6 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -133,7 +133,7 @@ static void au0828_irq_callback(struct urb *urb) au0828_isocdbg("au0828_irq_callback called: status kill\n"); return; default: /* unknown error */ - au0828_isocdbg("urb completition error %d.\n", urb->status); + au0828_isocdbg("urb completion error %d.\n", urb->status); break; } -- cgit v1.2.3-70-g09d2 From 4effa8bfe4f6bacd041f98775f49cdc550502dc2 Mon Sep 17 00:00:00 2001 From: Jasmin Jessich Date: Sat, 28 Jul 2018 09:54:49 -0400 Subject: media: i2c: fix warning in Aptina MT9V111 This fixes the "'idx' may be used uninitialized in this function" warning: drivers/media/i2c/mt9v111.c: In function 'mt9v111_set_format': drivers/media/i2c/mt9v111.c:887:15: warning: 'idx' may be used uninitialized in this function [-Wmaybe-uninitialized] unsigned int idx; ^~~ Signed-off-by: Jasmin Jessich Acked-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v111.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index da8f6ab91307..58d5f2224bff 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -884,7 +884,7 @@ static int mt9v111_set_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt new_fmt; struct v4l2_mbus_framefmt *__fmt; unsigned int best_fit = ~0L; - unsigned int idx; + unsigned int idx = 0; unsigned int i; mutex_lock(&mt9v111->stream_mutex); -- cgit v1.2.3-70-g09d2 From b07b684930fe0b8032bddba5aa60686011932019 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 06:06:17 -0400 Subject: media: v4l2-mem2mem: add descriptions to MC fields drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'source_pad' not described in 'v4l2_m2m_dev' drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'sink' not described in 'v4l2_m2m_dev' drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'sink_pad' not described in 'v4l2_m2m_dev' drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'proc' not described in 'v4l2_m2m_dev' drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'proc_pads' not described in 'v4l2_m2m_dev' drivers/media/v4l2-core/v4l2-mem2mem.c:90: warning: Function parameter or member 'intf_devnode' not described in 'v4l2_m2m_dev' Fixes: be2fff656322 ("media: add helpers for memory-to-memory media controller") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 0a93c5b173c2..ce9bd1b91210 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -66,6 +66,24 @@ static const char * const m2m_entity_name[] = { /** * struct v4l2_m2m_dev - per-device context + * @source: &struct media_entity pointer with the source entity + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @source_pad: &struct media_pad with the source pad. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @sink: &struct media_entity pointer with the sink entity + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @sink_pad: &struct media_pad with the sink pad. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @proc: &struct media_entity pointer with the M2M device itself. + * @proc_pads: &struct media_pad with the @proc pads. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @intf_devnode: &struct media_intf devnode pointer with the interface + * with controls the M2M device. * @curr_ctx: currently running instance * @job_queue: instances queued to run * @job_spinlock: protects job_queue -- cgit v1.2.3-70-g09d2 From 51be5a2b43f86af40b3fb78d7e4a22b5357fbc0d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 06:13:36 -0400 Subject: media: sta2x11: add a missing parameter description Fixes this warning: drivers/media/pci/sta2x11/sta2x11_vip.c:156: warning: Function parameter or member 'v4l_lock' not described in 'sta2x11_vip' Fixes: cd63c0288fd7 ("media: sta2x11: Add video_device and vb2_queue locks") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index da2b2a2292d0..1858efedaf1a 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -116,6 +116,7 @@ static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) * @sequence: sequence number of acquired buffer * @active: current active buffer * @lock: used in videobuf2 callback + * @v4l_lock: serialize its video4linux ioctls * @tcount: Number of top frames * @bcount: Number of bottom frames * @overflow: Number of FIFO overflows -- cgit v1.2.3-70-g09d2 From 75070c6a3311e320508427bbc779c4de6942872e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 06:17:00 -0400 Subject: media: vsp1_dl: add a description for cmdpool field Gets rid of this build warning: drivers/media/platform/vsp1/vsp1_dl.c:229: warning: Function parameter or member 'cmdpool' not described in 'vsp1_dl_manager' Fixes: f3b98e3c4d2e ("media: vsp1: Provide support for extended command pools") Reviewed-by: Kieran Bingham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 9255b5ee2cb8..26289adaf658 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -211,7 +211,7 @@ struct vsp1_dl_list { * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware * @pool: body pool for the display list bodies - * @autofld_cmds: command pool to support auto-fld interlaced mode + * @cmdpool: commands pool for extended display list */ struct vsp1_dl_manager { unsigned int index; -- cgit v1.2.3-70-g09d2 From d9a71866944967b8622c493ef1b570ccb74e45ae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 06:36:31 -0400 Subject: media: mt9v111: avoid going past the buffer As warned by smatch: drivers/media/i2c/mt9v111.c:854 mt9v111_enum_frame_size() error: buffer overflow 'mt9v111_frame_sizes' 5 <= 5 drivers/media/i2c/mt9v111.c:855 mt9v111_enum_frame_size() error: buffer overflow 'mt9v111_frame_sizes' 5 <= 5 drivers/media/i2c/mt9v111.c:856 mt9v111_enum_frame_size() error: buffer overflow 'mt9v111_frame_sizes' 5 <= 5 drivers/media/i2c/mt9v111.c:857 mt9v111_enum_frame_size() error: buffer overflow 'mt9v111_frame_sizes' 5 <= 5 Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v111.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 58d5f2224bff..70fad0940435 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -848,7 +848,7 @@ static int mt9v111_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->pad || fse->index > ARRAY_SIZE(mt9v111_frame_sizes)) + if (fse->pad || fse->index >= ARRAY_SIZE(mt9v111_frame_sizes)) return -EINVAL; fse->min_width = mt9v111_frame_sizes[fse->index].width; -- cgit v1.2.3-70-g09d2 From 845b978a871bff3707eee611b32e4be0b9a94dd2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 06:49:26 -0400 Subject: media: rtl28xxu: be sure that it won't go past the array size smatch warns that the RC query code could go past the array size: drivers/media/usb/dvb-usb-v2/rtl28xxu.c:1757 rtl2832u_rc_query() error: buffer overflow 'buf' 128 <= 130 drivers/media/usb/dvb-usb-v2/rtl28xxu.c:1758 rtl2832u_rc_query() error: buffer overflow 'buf' 128 <= 130 The driver logic gets the length of the IR RX buffer with: ret = rtl28xxu_rd_reg(d, IR_RX_BC, &buf[0]); ... len = buf[0]; In thesis, this could range between 0 and 255 [1]. While this should never happen in practice, due to hardware limits, smatch is right when it complains about that, as there's nothing at the logic that would prevent it. So, if for whatever reason, buf[0] gets filled by rtl28xx read functions with a value bigger than 128, it will go past the array. So, add an explicit check. [1] I've no idea why smatch thinks that the maximum value is 130. I double-checked the code several times. Was unable to find any reason for assuming 130. Perhaps smatch is not properly parsing u8 here? Fixes: b5cbaa43a676 ("[media] rtl28xx: initial support for rtl2832u") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c76e78f9638a..a970224a94bd 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1732,7 +1732,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) goto exit; ret = rtl28xxu_rd_reg(d, IR_RX_BC, &buf[0]); - if (ret) + if (ret || buf[0] > sizeof(buf)) goto err; len = buf[0]; -- cgit v1.2.3-70-g09d2 From 3354b54f9f7037a1122d3b6009aa9d39829d6843 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 07:29:12 -0400 Subject: media: vivid: shut up warnings due to a non-trivial logic The vivid driver uses a complex logic to save one kalloc/kfree allocation. That non-trivial way of allocating data causes smatch to warn: drivers/media/platform/vivid/vivid-core.c:869 vivid_create_instance() warn: potentially one past the end of array 'dev->query_dv_timings_qmenu[dev->query_dv_timings_size]' drivers/media/platform/vivid/vivid-core.c:869 vivid_create_instance() warn: potentially one past the end of array 'dev->query_dv_timings_qmenu[dev->query_dv_timings_size]' I also needed to read the code several times in order to understand what it was desired there. It turns that the logic was right, although confusing to read. As it is doing allocations on a non-standard way, let's add some documentation while shutting up the false positive. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 31db363602e5..4c235ea27987 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -647,6 +647,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) unsigned node_type = node_types[inst]; unsigned int allocator = allocators[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; + char *strings_base; int ret; int i; @@ -859,17 +860,33 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* create a string array containing the names of all the preset timings */ while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) dev->query_dv_timings_size++; + + /* + * In order to save one allocation and an extra free, let's optimize + * the allocation here: we'll use the first elements of the + * dev->query_dv_timings_qmenu to store the timing strings pointer, + * adding an extra space there to a store a string up to 32 bytes. + * So, instead of allocating an array with size of: + * dev->query_dv_timings_size * (sizeof(void *) + * it will allocate: + * dev->query_dv_timings_size * (sizeof(void *) + + * dev->query_dv_timings_size * 32 + */ dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size, (sizeof(void *) + 32), GFP_KERNEL); if (dev->query_dv_timings_qmenu == NULL) goto free_dev; + + /* Sets strings_base to be after the space to store the pointers */ + strings_base = ((char *)&dev->query_dv_timings_qmenu) + + dev->query_dv_timings_size * sizeof(void *); + for (i = 0; i < dev->query_dv_timings_size; i++) { const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size]; + char *p = strings_base + i * 32; u32 htot, vtot; - p += i * 32; dev->query_dv_timings_qmenu[i] = p; htot = V4L2_DV_BT_FRAME_WIDTH(bt); -- cgit v1.2.3-70-g09d2 From 40e431112c63296a6130810ab62a5fe73953f074 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 07:59:20 -0400 Subject: media: cleanup fall-through comments As Ian pointed out, adding a '-' to the fallthrough seems to meet the regex requirements at level 3 of the warning, at least when the comment fits into a single line. So, replace by a single line the comments that were broken into multiple lines just to make gcc -Wimplicit-fallthrough=3 happy. Suggested-by: Ian Arkver Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drx39xyj/drxj.c | 3 +-- drivers/media/dvb-frontends/drxd_hard.c | 6 ++---- drivers/media/dvb-frontends/drxk_hard.c | 18 ++++++------------ drivers/staging/media/imx/imx-media-csi.c | 3 +-- 4 files changed, 10 insertions(+), 20 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 2ddb7d218ace..2948d12d7c14 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2841,8 +2841,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o /* coef = 188/204 */ max_bit_rate = (ext_attr->curr_symbol_rate / 8) * nr_bits * 188; - /* pass through as b/c Annex A/c need following settings */ - /* fall-through */ + /* fall-through - as b/c Annex A/C need following settings */ case DRX_STANDARD_ITU_B: rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0); if (rc != 0) { diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 11fc259e4383..684d428efb0d 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1970,8 +1970,7 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->transmission_mode) { default: /* Not set, detect it automatically */ operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; - /* try first guess DRX_FFTMODE_8K */ - /* fall through */ + /* fall through - try first guess DRX_FFTMODE_8K */ case TRANSMISSION_MODE_8K: transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; if (state->type_A) { @@ -2144,8 +2143,7 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->modulation) { default: operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; - /* try first guess DRX_CONSTELLATION_QAM64 */ - /* fall through */ + /* fall through - try first guess DRX_CONSTELLATION_QAM64 */ case QAM_64: transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; if (state->type_A) { diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index ac10781d3550..f1886945a7bc 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -3270,13 +3270,11 @@ static int dvbt_sc_command(struct drxk_state *state, case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); - /* All commands using 1 parameters */ - /* fall through */ + /* fall through - All commands using 1 parameters */ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: case OFDM_SC_RA_RAM_CMD_USER_IO: status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); - /* All commands using 0 parameters */ - /* fall through */ + /* fall through - All commands using 0 parameters */ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: case OFDM_SC_RA_RAM_CMD_NULL: /* Write command */ @@ -3784,8 +3782,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case TRANSMISSION_MODE_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M; - /* try first guess DRX_FFTMODE_8K */ - /* fall through */ + /* fall through - try first guess DRX_FFTMODE_8K */ case TRANSMISSION_MODE_8K: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; break; @@ -3799,8 +3796,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, default: case GUARD_INTERVAL_AUTO: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M; - /* try first guess DRX_GUARD_1DIV4 */ - /* fall through */ + /* fall through - try first guess DRX_GUARD_1DIV4 */ case GUARD_INTERVAL_1_4: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; break; @@ -3841,8 +3837,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case QAM_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M; - /* try first guess DRX_CONSTELLATION_QAM64 */ - /* fall through */ + /* fall through - try first guess DRX_CONSTELLATION_QAM64 */ case QAM_64: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; break; @@ -3885,8 +3880,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case FEC_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M; - /* try first guess DRX_CODERATE_2DIV3 */ - /* fall through */ + /* fall through - try first guess DRX_CODERATE_2DIV3 */ case FEC_2_3: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; break; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index b7ffd231c64b..cd2c291e1e94 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -460,8 +460,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_cycles = incc->cycles; break; } - /* for non-passthrough RGB565 (CSI-2 bus) */ - /* Falls through */ + /* fallthrough - non-passthrough RGB565 (CSI-2 bus) */ default: burst_size = (image.pix.width & 0xf) ? 8 : 16; passthrough_bits = 16; -- cgit v1.2.3-70-g09d2 From 3fcb3c836ef413d3fc848288b308eb655e08d853 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 08:10:34 -0400 Subject: media: tuner-xc2028: don't use casts for printing sizes Makes smatch happier by using %zd instead of casting sizes: drivers/media/tuners/tuner-xc2028.c:378 load_all_firmwares() warn: argument 4 to %d specifier is cast from pointer drivers/media/tuners/tuner-xc2028.c:619 load_firmware() warn: argument 6 to %d specifier is cast from pointer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-xc2028.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 222b93ef31c0..aa6861dcd3fd 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -376,9 +376,8 @@ static int load_all_firmwares(struct dvb_frontend *fe, tuner_err("Firmware type "); dump_firm_type(type); printk(KERN_CONT - "(%x), id %llx is corrupted (size=%d, expected %d)\n", - type, (unsigned long long)id, - (unsigned)(endp - p), size); + "(%x), id %llx is corrupted (size=%zd, expected %d)\n", + type, (unsigned long long)id, (endp - p), size); goto corrupt; } @@ -616,8 +615,8 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type, } if ((size + p > endp)) { - tuner_err("missing bytes: need %d, have %d\n", - size, (int)(endp - p)); + tuner_err("missing bytes: need %d, have %zd\n", + size, (endp - p)); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From c4f047969fa1707e1312e6600b630aef516e13c6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 08:14:43 -0400 Subject: media: drxj: get rid of uneeded casts Instead of doing casts, use %zd to print sizes, in order to make smatch happier: drivers/media/dvb-frontends/drx39xyj/drxj.c:11814 drx_ctrl_u_code() warn: argument 4 to %u specifier is cast from pointer drivers/media/dvb-frontends/drx39xyj/drxj.c:11845 drx_ctrl_u_code() warn: argument 3 to %u specifier is cast from pointer drivers/media/dvb-frontends/drx39xyj/drxj.c:11869 drx_ctrl_u_code() warn: argument 3 to %u specifier is cast from pointer drivers/media/dvb-frontends/drx39xyj/drxj.c:11878 drx_ctrl_u_code() warn: argument 3 to %u specifier is cast from pointer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drx39xyj/drxj.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 2948d12d7c14..9628d4067fe1 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -11810,8 +11810,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); - pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", - (unsigned)(mc_data - mc_data_init), block_hdr.addr, + pr_debug("%zd: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + (mc_data - mc_data_init), block_hdr.addr, block_hdr.size, block_hdr.flags, block_hdr.CRC); /* Check block header on: @@ -11841,8 +11841,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, mc_block_nr_bytes, mc_data, 0x0000)) { rc = -EIO; - pr_err("error writing firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error writing firmware at pos %zd\n", + mc_data - mc_data_init); goto release; } break; @@ -11865,8 +11865,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, (u16)bytes_to_comp, (u8 *)mc_data_buffer, 0x0000)) { - pr_err("error reading firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error reading firmware at pos %zd\n", + mc_data - mc_data_init); return -EIO; } @@ -11874,8 +11874,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, bytes_to_comp); if (result) { - pr_err("error verifying firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error verifying firmware at pos %zd\n", + mc_data - mc_data_init); return -EIO; } -- cgit v1.2.3-70-g09d2 From 8218840f76a525f014f404b5e97d9523e327f4b0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 08:16:09 -0400 Subject: media: xc4000: get rid of uneeded casts Instead of doing casts, use %zd to print sizes, in order to make smatch happier: drivers/media/tuners/xc4000.c:818 xc4000_fwupload() warn: argument 4 to %d specifier is cast from pointer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 76b3f37f24a8..eb6d65dae748 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -815,9 +815,9 @@ static int xc4000_fwupload(struct dvb_frontend *fe) p += sizeof(size); if (!size || size > endp - p) { - printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n", + printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%zd, expected %d)\n", type, (unsigned long long)id, - (unsigned)(endp - p), size); + endp - p, size); goto corrupt; } -- cgit v1.2.3-70-g09d2 From 484f9b372dd8da6a4a9867ebcd10e5c2b21ab478 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 7 Aug 2018 11:38:14 -0400 Subject: media: mt9v111: Fix build error with no VIDEO_V4L2_SUBDEV_API The v4l2_subdev_get_try_format() function is only defined if the VIDEO_V4L2_SUBDEV_API Kconfig option is enabled. Builds configured without that symbol fails with: drivers/media/i2c/mt9v111.c:801:10: error: implicit declaration of function 'v4l2_subdev_get_try_format'; Fix this by protecting the function call by testing for the right symbol. media: mt9v111: fix random build errors Fix the internal check for it to do the right thing if the subdev API is not built. Fixes: aab7ed1c ("media: i2c: Add driver for Aptina MT9V111") Reported-by: Randy Dunlap Reported-by: kbuild test robot Signed-off-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v111.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 70fad0940435..b5410aeb5fe2 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -797,7 +797,7 @@ static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) +#if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) return v4l2_subdev_get_try_format(&mt9v111->sd, cfg, pad); #else return &cfg->try_fmt; -- cgit v1.2.3-70-g09d2 From 541b647a34d36827bf06aa64c3a5e5936cffa10f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 09:15:27 -0400 Subject: media: exynos-gsc: fix return code if mutex was interrupted All poll routines expect a poll flag, and not error codes. So, instead of returning -ERESTARTSYS if the mutex got interrupted, return EPOLERR, just like the V4L2 VB2 code. Solves this sparce warning: drivers/media/platform/exynos-gsc/gsc-m2m.c:716:24: warning: incorrect type in return expression (different base types) drivers/media/platform/exynos-gsc/gsc-m2m.c:716:24: expected restricted __poll_t drivers/media/platform/exynos-gsc/gsc-m2m.c:716:24: got int Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e9ff27949a91..c9d2f6c5311a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -713,7 +713,7 @@ static __poll_t gsc_m2m_poll(struct file *file, __poll_t ret; if (mutex_lock_interruptible(&gsc->lock)) - return -ERESTARTSYS; + return EPOLLERR; ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); mutex_unlock(&gsc->lock); -- cgit v1.2.3-70-g09d2 From 9de1be6ec05a635079fbcf75c5d47f656b61fe4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 09:18:26 -0400 Subject: media: saa7164: fix return codes for the polling routine All poll handlers should return a poll flag, and not error codes. So, instead of returning an error, do the right thing at saa7164, e. g. to return EPOLERR on errors, just like the V4L2 VB2 code. Solves the following sparse warnings: drivers/media/pci/saa7164/saa7164-vbi.c:632:24: warning: incorrect type in return expression (different base types) drivers/media/pci/saa7164/saa7164-vbi.c:632:24: expected restricted __poll_t drivers/media/pci/saa7164/saa7164-vbi.c:632:24: got int drivers/media/pci/saa7164/saa7164-vbi.c:637:40: warning: incorrect type in return expression (different base types) drivers/media/pci/saa7164/saa7164-vbi.c:637:40: expected restricted __poll_t drivers/media/pci/saa7164/saa7164-vbi.c:637:40: got int drivers/media/pci/saa7164/saa7164-vbi.c:647:40: warning: incorrect type in return expression (different base types) drivers/media/pci/saa7164/saa7164-vbi.c:647:40: expected restricted __poll_t drivers/media/pci/saa7164/saa7164-vbi.c:647:40: got int Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-vbi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c index 64ab91c24c18..221de91a8bae 100644 --- a/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/drivers/media/pci/saa7164/saa7164-vbi.c @@ -629,12 +629,12 @@ static __poll_t fops_poll(struct file *file, poll_table *wait) port->last_poll_msecs_diff); if (!video_is_registered(port->v4l_device)) - return -EIO; + return EPOLLERR; if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { if (atomic_inc_return(&port->v4l_reader_count) == 1) { if (saa7164_vbi_initialize(port) < 0) - return -EINVAL; + return EPOLLERR; saa7164_vbi_start_streaming(port); msleep(200); } @@ -644,7 +644,7 @@ static __poll_t fops_poll(struct file *file, poll_table *wait) if ((file->f_flags & O_NONBLOCK) == 0) { if (wait_event_interruptible(port->wait_read, saa7164_vbi_next_buf(port))) { - return -ERESTARTSYS; + return EPOLLERR; } } -- cgit v1.2.3-70-g09d2 From c46aa8491dd40d4ede5c9196c20d78e1e8c27786 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 09:23:33 -0400 Subject: media: s3c-camif: fix return code for the polling routine All poll handlers should return a poll flag, and not error codes. So, instead of returning an error, do the right thing here, e. g. to return EPOLERR on errors, just like the V4L2 VB2 code. Solves the following sparse warning: drivers/media/platform/s3c-camif/camif-capture.c:604:21: warning: incorrect type in assignment (different base types) drivers/media/platform/s3c-camif/camif-capture.c:604:21: expected restricted __poll_t [usertype] ret drivers/media/platform/s3c-camif/camif-capture.c:604:21: got int Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index b1d9f3857d3d..c02dce8b4c6c 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -601,7 +601,7 @@ static __poll_t s3c_camif_poll(struct file *file, mutex_lock(&camif->lock); if (vp->owner && vp->owner != file->private_data) - ret = -EBUSY; + ret = EPOLLERR; else ret = vb2_poll(&vp->vb_queue, file, wait); -- cgit v1.2.3-70-g09d2 From dcefa533bd97922a3cac37f395247082be44600b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 09:25:37 -0400 Subject: media: radio-wl1273: fix return code for the polling routine All poll handlers should return a poll flag, and not error codes. So, instead of returning an error, do the right thing here, e. g. to return EPOLERR on errors, just like the V4L2 VB2 code. Solves the following sparse warning: drivers/media/radio/radio-wl1273.c:1099:24: warning: incorrect type in return expression (different base types) drivers/media/radio/radio-wl1273.c:1099:24: expected restricted __poll_t drivers/media/radio/radio-wl1273.c:1099:24: got int Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 8f9f8dfc3497..11aa94f189cb 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1096,7 +1096,7 @@ static __poll_t wl1273_fm_fops_poll(struct file *file, struct wl1273_core *core = radio->core; if (radio->owner && radio->owner != file) - return -EBUSY; + return EPOLLERR; radio->owner = file; -- cgit v1.2.3-70-g09d2 From 6c33d826b5c468033c21d8653d148dd758bbab28 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Aug 2018 09:48:36 -0400 Subject: media: isp: fix a warning about a wrong struct initializer As sparse complains: drivers/media/platform/omap3isp/isp.c:303:39: warning: Using plain integer as NULL pointer when a struct is initialized with { 0 }, actually the first element of the struct is initialized with zeros, initializing the other elements recursively. That can even generate gcc warnings on nested structs. So, instead, use the gcc-specific syntax for that (with is used broadly inside the Kernel), initializing it with {}; Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 03354d513311..842e2235047d 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -300,7 +300,7 @@ static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) static int isp_xclk_init(struct isp_device *isp) { struct device_node *np = isp->dev->of_node; - struct clk_init_data init = { 0 }; + struct clk_init_data init = {}; unsigned int i; for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) -- cgit v1.2.3-70-g09d2 From 8179de98cd35242446f4c7a6ecdd73c6c41f0cfb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 8 Aug 2018 09:54:49 -0400 Subject: siano: get rid of an unused return code for debugfs register The siano's debugfs register logic is optional: it should be ok if it fails. So, no need to check if debufs register succeeded. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/siano/smsdvb-debugfs.c | 10 +++++----- drivers/media/common/siano/smsdvb.h | 7 ++----- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c index 40891f4f842b..c95d4583498e 100644 --- a/drivers/media/common/siano/smsdvb-debugfs.c +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -500,7 +500,7 @@ void smsdvb_debugfs_release(struct smsdvb_client_t *client) client->debugfs = NULL; } -int smsdvb_debugfs_register(void) +void smsdvb_debugfs_register(void) { struct dentry *d; @@ -517,15 +517,15 @@ int smsdvb_debugfs_register(void) d = debugfs_create_dir("smsdvb", usb_debug_root); if (IS_ERR_OR_NULL(d)) { pr_err("Couldn't create sysfs node for smsdvb\n"); - return PTR_ERR(d); - } else { - smsdvb_debugfs_usb_root = d; + return; } - return 0; + smsdvb_debugfs_usb_root = d; } void smsdvb_debugfs_unregister(void) { + if (!smsdvb_debugfs_usb_root) + return; debugfs_remove_recursive(smsdvb_debugfs_usb_root); smsdvb_debugfs_usb_root = NULL; } diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h index b15754d95ec0..befeb9817e54 100644 --- a/drivers/media/common/siano/smsdvb.h +++ b/drivers/media/common/siano/smsdvb.h @@ -107,7 +107,7 @@ struct RECEPTION_STATISTICS_PER_SLICES_S { int smsdvb_debugfs_create(struct smsdvb_client_t *client); void smsdvb_debugfs_release(struct smsdvb_client_t *client); -int smsdvb_debugfs_register(void); +void smsdvb_debugfs_register(void); void smsdvb_debugfs_unregister(void); #else @@ -119,10 +119,7 @@ static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client) static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {} -static inline int smsdvb_debugfs_register(void) -{ - return 0; -}; +static inline void smsdvb_debugfs_register(void) {} static inline void smsdvb_debugfs_unregister(void) {}; -- cgit v1.2.3-70-g09d2 From da2048b7348a0be92f706ac019e022139e29495e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Aug 2018 15:06:18 -0400 Subject: Revert "media: vivid: shut up warnings due to a non-trivial logic" 0day kernel testing robot got the below dmesg and the first bad commit is https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master commit 3354b54f9f7037a1122d3b6009aa9d39829d6843 [ 248.847809] BUG: unable to handle kernel paging request at ffffc90000393131 [ 248.848015] Call Trace: [ 248.848015] ? vivid_dev_release+0xc0/0xc0 [ 248.848015] ? acpi_dev_pm_attach+0x27/0xd0 This reverts commit 3354b54f9f7037a1122d3b6009aa9d39829d6843. --- drivers/media/platform/vivid/vivid-core.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 4c235ea27987..31db363602e5 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -647,7 +647,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) unsigned node_type = node_types[inst]; unsigned int allocator = allocators[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; - char *strings_base; int ret; int i; @@ -860,33 +859,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* create a string array containing the names of all the preset timings */ while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) dev->query_dv_timings_size++; - - /* - * In order to save one allocation and an extra free, let's optimize - * the allocation here: we'll use the first elements of the - * dev->query_dv_timings_qmenu to store the timing strings pointer, - * adding an extra space there to a store a string up to 32 bytes. - * So, instead of allocating an array with size of: - * dev->query_dv_timings_size * (sizeof(void *) - * it will allocate: - * dev->query_dv_timings_size * (sizeof(void *) + - * dev->query_dv_timings_size * 32 - */ dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size, (sizeof(void *) + 32), GFP_KERNEL); if (dev->query_dv_timings_qmenu == NULL) goto free_dev; - - /* Sets strings_base to be after the space to store the pointers */ - strings_base = ((char *)&dev->query_dv_timings_qmenu) - + dev->query_dv_timings_size * sizeof(void *); - for (i = 0; i < dev->query_dv_timings_size; i++) { const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - char *p = strings_base + i * 32; + char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size]; u32 htot, vtot; + p += i * 32; dev->query_dv_timings_qmenu[i] = p; htot = V4L2_DV_BT_FRAME_WIDTH(bt); -- cgit v1.2.3-70-g09d2