From 445093ff76a0b01628637b5890ac332507d8a197 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sat, 9 May 2020 17:29:03 +0200 Subject: media: staging: rkisp1 stats: set a measure flag with '|=' instead of '=' The flag RKISP1_CIF_ISP_STAT_AFM_FIN that indicates a type of statistics is mistakenly set with '=' instead of '|=' this might set off previous flags. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 6dfcbdc3deb8..8351bda0be03 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -253,7 +253,7 @@ static void rkisp1_stats_get_afc_meas(struct rkisp1_stats *stats, struct rkisp1_device *rkisp1 = stats->rkisp1; struct rkisp1_cif_isp_af_stat *af; - pbuf->meas_type = RKISP1_CIF_ISP_STAT_AFM_FIN; + pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; af = &pbuf->params.af; af->window[0].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_A); -- cgit v1.2.3 From 3bee86168657d3d846a12b6687d362d361e0cd35 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sat, 9 May 2020 17:29:04 +0200 Subject: media: staging: rkisp1: stats: don't set stats flags in rkisp1_stats_send_measurement The flags that indicate which statistics are read are already set in the functions that read them so there is no need to set them in the function rkisp1_stats_send_measurement. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 8351bda0be03..0616793ae395 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -356,26 +356,19 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, cur_stat_buf = (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AWB; - } - if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) rkisp1_stats_get_afc_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; - } if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) { rkisp1_stats_get_aec_meas(stats, cur_stat_buf); rkisp1_stats_get_bls_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP; } - if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) rkisp1_stats_get_hst_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_HIST; - } vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, sizeof(struct rkisp1_stat_buffer)); -- cgit v1.2.3 From 0c8bc934ee304e43daa04492bced568dd0bde594 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 22 May 2020 13:56:33 +0200 Subject: media: staging: rkisp1: fix dev param for dev_* debugs In some debug prints a wrong 'dev' argument is used for the dev_(dbg/err/warn), this cause a prefix "(NULL device *)" to the prints. In some prints the 'dev' of the sensor subdevice is used which is also wrong. Signed-off-by: Dafna Hirschfeld Reviewed-by: Laurent Pinchart [hverkuil-cisco@xs4all.nl: s/in subdev/in sensor subdev/ as per Laurent's comment] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 2 +- drivers/staging/media/rkisp1/rkisp1-isp.c | 17 ++++++++++------- drivers/staging/media/rkisp1/rkisp1-params.c | 2 +- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 9ac38bafb839..f38801fea10d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -129,7 +129,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); if (ret < 0) { - dev_err(sd->dev, "failed to find src pad for %s\n", + dev_err(rkisp1->dev, "failed to find src pad for %s\n", sd->name); return ret; } diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index dc2b59a0160a..6763379d6a65 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -203,10 +203,8 @@ static struct v4l2_subdev *rkisp1_get_remote_sensor(struct v4l2_subdev *sd) local = &sd->entity.pads[RKISP1_ISP_PAD_SINK_VIDEO]; remote = media_entity_remote_pad(local); - if (!remote) { - dev_warn(sd->dev, "No link between isp and sensor\n"); + if (!remote) return NULL; - } sensor_me = remote->entity; return media_entity_to_v4l2_subdev(sensor_me); @@ -889,18 +887,20 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { static int rkisp1_mipi_csi2_start(struct rkisp1_isp *isp, struct rkisp1_sensor_async *sensor) { + struct rkisp1_device *rkisp1 = + container_of(isp->sd.v4l2_dev, struct rkisp1_device, v4l2_dev); union phy_configure_opts opts; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; s64 pixel_clock; if (!sensor->pixel_rate_ctrl) { - dev_warn(sensor->sd->dev, "No pixel rate control in subdev\n"); + dev_warn(rkisp1->dev, "No pixel rate control in sensor subdev\n"); return -EPIPE; } pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl); if (!pixel_clock) { - dev_err(sensor->sd->dev, "Invalid pixel rate value\n"); + dev_err(rkisp1->dev, "Invalid pixel rate value\n"); return -EINVAL; } @@ -933,8 +933,11 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } sensor_sd = rkisp1_get_remote_sensor(sd); - if (!sensor_sd) + if (!sensor_sd) { + dev_warn(rkisp1->dev, "No link between isp and sensor\n"); return -ENODEV; + } + rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); @@ -1021,7 +1024,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { - dev_err(sd->dev, "Failed to register isp subdev\n"); + dev_err(rkisp1->dev, "Failed to register isp subdev\n"); goto err_cleanup_media_entity; } diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 44d542caf32b..797e79de659c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1607,7 +1607,7 @@ int rkisp1_params_register(struct rkisp1_params *params, goto err_release_queue; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { - dev_err(&vdev->dev, + dev_err(rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); goto err_cleanup_media_entity; } diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index d049374413dc..26fb41053f56 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -639,7 +639,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&rsz->ops_lock); -- cgit v1.2.3 From 7dcfb327f8fd7418124fc6d63bdce91b5844f50a Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 08:27:45 +0200 Subject: media: staging: tegra-vde: fix runtime pm imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even the call returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index d3e63512a765..3fdf2cd0b99e 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -777,7 +777,7 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, ret = pm_runtime_get_sync(dev); if (ret < 0) - goto unlock; + goto put_runtime_pm; /* * We rely on the VDE registers reset value, otherwise VDE @@ -843,8 +843,6 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, put_runtime_pm: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - -unlock: mutex_unlock(&vde->lock); release_dpb_frames: -- cgit v1.2.3 From 372faeb46b91be672fb0f03f7b358759bcd4aed8 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 15 May 2020 16:29:52 +0200 Subject: media: staging: rkisp1: cap: remove support of BGR666 format The rkisp1 supports RGB encoding with 6 bits per color with the following format: - - b5 b4 b3 b2 b1 b0 - - g5 g4 g3 g2 g1 g0 - - r5 r4 r3 r2 r1 r0 - - - - - - - - This is not how V4L2_PIX_FMT_BGR666 is defined, so remove this format from the driver's formats list. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index f69235f82c45..793ec884c894 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -283,10 +283,6 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = { .fourcc = V4L2_PIX_FMT_RGB565, .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_BGR666, - .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, - .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB666, }, }; -- cgit v1.2.3 From e7eab49132ba819632c3bb9cd5b8342f2cdeb939 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:20 +0200 Subject: media: staging/media/soc_camera: remove this driver The soc_camera driver (and related soc_camera-dependent sensor drivers) is obsolete and depends on BROKEN for a long time now. Nobody is using it, so it is time to kill it off. Signed-off-by: Hans Verkuil Cc: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/soc_camera/Kconfig | 51 - drivers/staging/media/soc_camera/Makefile | 7 - drivers/staging/media/soc_camera/TODO | 4 - drivers/staging/media/soc_camera/imx074.c | 492 ------ drivers/staging/media/soc_camera/mt9t031.c | 853 --------- drivers/staging/media/soc_camera/soc-camera.rst | 171 -- drivers/staging/media/soc_camera/soc_camera.c | 2164 ----------------------- drivers/staging/media/soc_camera/soc_mediabus.c | 529 ------ drivers/staging/media/soc_camera/soc_mt9v022.c | 1008 ----------- drivers/staging/media/soc_camera/soc_ov5642.c | 1085 ------------ drivers/staging/media/soc_camera/soc_ov9740.c | 992 ----------- include/media/drv-intf/soc_mediabus.h | 107 -- 14 files changed, 7466 deletions(-) delete mode 100644 drivers/staging/media/soc_camera/Kconfig delete mode 100644 drivers/staging/media/soc_camera/Makefile delete mode 100644 drivers/staging/media/soc_camera/TODO delete mode 100644 drivers/staging/media/soc_camera/imx074.c delete mode 100644 drivers/staging/media/soc_camera/mt9t031.c delete mode 100644 drivers/staging/media/soc_camera/soc-camera.rst delete mode 100644 drivers/staging/media/soc_camera/soc_camera.c delete mode 100644 drivers/staging/media/soc_camera/soc_mediabus.c delete mode 100644 drivers/staging/media/soc_camera/soc_mt9v022.c delete mode 100644 drivers/staging/media/soc_camera/soc_ov5642.c delete mode 100644 drivers/staging/media/soc_camera/soc_ov9740.c delete mode 100644 include/media/drv-intf/soc_mediabus.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 4bb1eca6f597..71d077762698 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig" source "drivers/staging/media/ipu3/Kconfig" -source "drivers/staging/media/soc_camera/Kconfig" - source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" source "drivers/staging/media/rkisp1/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 71a47b61836d..17ececa1e095 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig deleted file mode 100644 index 4a54db121574..000000000000 --- a/drivers/staging/media/soc_camera/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config SOC_CAMERA - tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN - select VIDEOBUF2_CORE - help - SoC Camera is a common API to several cameras, not connecting - over a bus like PCI or USB. For example some i2c camera connected - directly to the data bus of an SoC. - -comment "soc_camera sensor drivers" - -config SOC_CAMERA_MT9M111 - tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" - depends on SOC_CAMERA && I2C - select VIDEO_MT9M111 - help - This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina. - This is the legacy configuration which shouldn't be used anymore, - while VIDEO_MT9M111 should be used instead. - -config SOC_CAMERA_MT9V022 - tristate "mt9v022 and mt9v024 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9V022 cameras from Micron - -config SOC_CAMERA_OV5642 - tristate "ov5642 camera support" - depends on SOC_CAMERA && I2C - help - This is a V4L2 camera driver for the OmniVision OV5642 sensor - -config SOC_CAMERA_OV9740 - tristate "ov9740 camera support" - depends on SOC_CAMERA && I2C - help - This is a ov9740 camera driver - -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony - -config SOC_CAMERA_MT9T031 - tristate "mt9t031 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile deleted file mode 100644 index 3a351bd629f5..000000000000 --- a/drivers/staging/media/soc_camera/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o -obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/soc_camera/TODO b/drivers/staging/media/soc_camera/TODO deleted file mode 100644 index 932af6443b67..000000000000 --- a/drivers/staging/media/soc_camera/TODO +++ /dev/null @@ -1,4 +0,0 @@ -The SoC camera framework is obsolete and scheduled for removal in the near -future. Developers are encouraged to convert the drivers to use the -regular V4L2 API if these drivers are still needed (and if someone has the -hardware). diff --git a/drivers/staging/media/soc_camera/imx074.c b/drivers/staging/media/soc_camera/imx074.c deleted file mode 100644 index 14240b74cdd0..000000000000 --- a/drivers/staging/media/soc_camera/imx074.c +++ /dev/null @@ -1,492 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for IMX074 CMOS Image Sensor from Sony - * - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Partially inspired by the IMX074 driver from the Android / MSM tree - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* IMX074 registers */ - -#define MODE_SELECT 0x0100 -#define IMAGE_ORIENTATION 0x0101 -#define GROUPED_PARAMETER_HOLD 0x0104 - -/* Integration Time */ -#define COARSE_INTEGRATION_TIME_HI 0x0202 -#define COARSE_INTEGRATION_TIME_LO 0x0203 -/* Gain */ -#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 -#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 - -/* PLL registers */ -#define PRE_PLL_CLK_DIV 0x0305 -#define PLL_MULTIPLIER 0x0307 -#define PLSTATIM 0x302b -#define VNDMY_ABLMGSHLMT 0x300a -#define Y_OPBADDR_START_DI 0x3014 -/* mode setting */ -#define FRAME_LENGTH_LINES_HI 0x0340 -#define FRAME_LENGTH_LINES_LO 0x0341 -#define LINE_LENGTH_PCK_HI 0x0342 -#define LINE_LENGTH_PCK_LO 0x0343 -#define YADDR_START 0x0347 -#define YADDR_END 0x034b -#define X_OUTPUT_SIZE_MSB 0x034c -#define X_OUTPUT_SIZE_LSB 0x034d -#define Y_OUTPUT_SIZE_MSB 0x034e -#define Y_OUTPUT_SIZE_LSB 0x034f -#define X_EVEN_INC 0x0381 -#define X_ODD_INC 0x0383 -#define Y_EVEN_INC 0x0385 -#define Y_ODD_INC 0x0387 - -#define HMODEADD 0x3001 -#define VMODEADD 0x3016 -#define VAPPLINE_START 0x3069 -#define VAPPLINE_END 0x306b -#define SHUTTER 0x3086 -#define HADDAVE 0x30e8 -#define LANESEL 0x3301 - -/* IMX074 supported geometry */ -#define IMX074_WIDTH 1052 -#define IMX074_HEIGHT 780 - -/* IMX074 has only one fixed colorspace per pixelcode */ -struct imx074_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct imx074 { - struct v4l2_subdev subdev; - const struct imx074_datafmt *fmt; - struct v4l2_clk *clk; -}; - -static const struct imx074_datafmt imx074_colour_fmts[] = { - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static struct imx074 *to_imx074(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct imx074, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct imx074_datafmt *imx074_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) - if (imx074_colour_fmts[i].code == code) - return imx074_colour_fmts + i; - - return NULL; -} - -static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) -{ - struct i2c_adapter *adap = client->adapter; - struct i2c_msg msg; - unsigned char tx[3]; - int ret; - - msg.addr = client->addr; - msg.buf = tx; - msg.len = 3; - msg.flags = 0; - - tx[0] = addr >> 8; - tx[1] = addr & 0xff; - tx[2] = data; - - ret = i2c_transfer(adap, &msg, 1); - - mdelay(2); - - return ret == 1 ? 0 : -EIO; -} - -static int reg_read(struct i2c_client *client, const u16 addr) -{ - u8 buf[2] = {addr >> 8, addr & 0xff}; - int ret; - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 2, - .buf = buf, - }, - }; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) { - dev_warn(&client->dev, "Reading register %x from %x failed\n", - addr, client->addr); - return ret; - } - - return buf[0] & 0xff; /* no sign-extension */ -} - -static int imx074_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - if (format->pad) - return -EINVAL; - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - if (!fmt) { - /* MIPI CSI could have changed the format, double-check */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = imx074_colour_fmts[0].code; - mf->colorspace = imx074_colour_fmts[0].colorspace; - } - - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - - return 0; -} - -static int imx074_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 imx074 *priv = to_imx074(client); - - const struct imx074_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int imx074_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX074_WIDTH; - sel->r.height = IMX074_HEIGHT; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - return 0; - default: - return -EINVAL; - } -} - -static int imx074_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || - (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) - return -EINVAL; - - code->code = imx074_colour_fmts[code->index].code; - return 0; -} - -static int imx074_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* MODE_SELECT: stream or standby */ - return reg_write(client, MODE_SELECT, !!enable); -} - -static int imx074_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 imx074 *priv = to_imx074(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int imx074_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { - .s_stream = imx074_s_stream, - .g_mbus_config = imx074_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { - .s_power = imx074_s_power, -}; - -static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { - .enum_mbus_code = imx074_enum_mbus_code, - .get_selection = imx074_get_selection, - .get_fmt = imx074_get_fmt, - .set_fmt = imx074_set_fmt, -}; - -static const struct v4l2_subdev_ops imx074_subdev_ops = { - .core = &imx074_subdev_core_ops, - .video = &imx074_subdev_video_ops, - .pad = &imx074_subdev_pad_ops, -}; - -static int imx074_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u16 id; - - ret = imx074_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, 0); - if (ret < 0) - goto done; - - id = ret << 8; - - ret = reg_read(client, 1); - if (ret < 0) - goto done; - - id |= ret; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x74) { - ret = -ENODEV; - goto done; - } - - /* PLL Setting EXTCLK=24MHz, 22.5times */ - reg_write(client, PLL_MULTIPLIER, 0x2D); - reg_write(client, PRE_PLL_CLK_DIV, 0x02); - reg_write(client, PLSTATIM, 0x4B); - - /* 2-lane mode */ - reg_write(client, 0x3024, 0x00); - - reg_write(client, IMAGE_ORIENTATION, 0x00); - - /* select RAW mode: - * 0x08+0x08 = top 8 bits - * 0x0a+0x08 = compressed 8-bits - * 0x0a+0x0a = 10 bits - */ - reg_write(client, 0x0112, 0x08); - reg_write(client, 0x0113, 0x08); - - /* Base setting for High frame mode */ - reg_write(client, VNDMY_ABLMGSHLMT, 0x80); - reg_write(client, Y_OPBADDR_START_DI, 0x08); - reg_write(client, 0x3015, 0x37); - reg_write(client, 0x301C, 0x01); - reg_write(client, 0x302C, 0x05); - reg_write(client, 0x3031, 0x26); - reg_write(client, 0x3041, 0x60); - reg_write(client, 0x3051, 0x24); - reg_write(client, 0x3053, 0x34); - reg_write(client, 0x3057, 0xC0); - reg_write(client, 0x305C, 0x09); - reg_write(client, 0x305D, 0x07); - reg_write(client, 0x3060, 0x30); - reg_write(client, 0x3065, 0x00); - reg_write(client, 0x30AA, 0x08); - reg_write(client, 0x30AB, 0x1C); - reg_write(client, 0x30B0, 0x32); - reg_write(client, 0x30B2, 0x83); - reg_write(client, 0x30D3, 0x04); - reg_write(client, 0x3106, 0x78); - reg_write(client, 0x310C, 0x82); - reg_write(client, 0x3304, 0x05); - reg_write(client, 0x3305, 0x04); - reg_write(client, 0x3306, 0x11); - reg_write(client, 0x3307, 0x02); - reg_write(client, 0x3308, 0x0C); - reg_write(client, 0x3309, 0x06); - reg_write(client, 0x330A, 0x08); - reg_write(client, 0x330B, 0x04); - reg_write(client, 0x330C, 0x08); - reg_write(client, 0x330D, 0x06); - reg_write(client, 0x330E, 0x01); - reg_write(client, 0x3381, 0x00); - - /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ - /* 1608 = 1560 + 48 (black lines) */ - reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); - reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); - reg_write(client, YADDR_START, 0x00); - reg_write(client, YADDR_END, 0x2F); - /* 0x838 == 2104 */ - reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); - reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); - /* 0x618 == 1560 */ - reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); - reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); - reg_write(client, X_EVEN_INC, 0x01); - reg_write(client, X_ODD_INC, 0x03); - reg_write(client, Y_EVEN_INC, 0x01); - reg_write(client, Y_ODD_INC, 0x03); - reg_write(client, HMODEADD, 0x00); - reg_write(client, VMODEADD, 0x16); - reg_write(client, VAPPLINE_START, 0x24); - reg_write(client, VAPPLINE_END, 0x53); - reg_write(client, SHUTTER, 0x00); - reg_write(client, HADDAVE, 0x80); - - reg_write(client, LANESEL, 0x00); - - reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ - - ret = 0; - -done: - imx074_s_power(subdev, 0); - return ret; -} - -static int imx074_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct imx074 *priv; - struct i2c_adapter *adapter = client->adapter; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "IMX074: missing platform data!\n"); - return -EINVAL; - } - - 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; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - - priv->fmt = &imx074_colour_fmts[0]; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); - return -EPROBE_DEFER; - } - - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto epwrinit; - - ret = imx074_video_probe(client); - if (ret < 0) - goto eprobe; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; - -epwrinit: -eprobe: - v4l2_clk_put(priv->clk); - return ret; -} - -static int imx074_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id imx074_id[] = { - { "imx074", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, imx074_id); - -static struct i2c_driver imx074_i2c_driver = { - .driver = { - .name = "imx074", - }, - .probe = imx074_probe, - .remove = imx074_remove, - .id_table = imx074_id, -}; - -module_i2c_driver(imx074_i2c_driver); - -MODULE_DESCRIPTION("Sony IMX074 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c deleted file mode 100644 index c14f23221544..000000000000 --- a/drivers/staging/media/soc_camera/mt9t031.c +++ /dev/null @@ -1,853 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9T031 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * ATTENTION: this driver still cannot be used outside of the soc-camera - * framework because of its PM implementation, using the video_device node. - * If hardware becomes available for testing, alternative PM approaches shall - * be considered and tested. - */ - -/* - * mt9t031 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9t031 selected register addresses */ -#define MT9T031_CHIP_VERSION 0x00 -#define MT9T031_ROW_START 0x01 -#define MT9T031_COLUMN_START 0x02 -#define MT9T031_WINDOW_HEIGHT 0x03 -#define MT9T031_WINDOW_WIDTH 0x04 -#define MT9T031_HORIZONTAL_BLANKING 0x05 -#define MT9T031_VERTICAL_BLANKING 0x06 -#define MT9T031_OUTPUT_CONTROL 0x07 -#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9T031_SHUTTER_WIDTH 0x09 -#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9T031_FRAME_RESTART 0x0b -#define MT9T031_SHUTTER_DELAY 0x0c -#define MT9T031_RESET 0x0d -#define MT9T031_READ_MODE_1 0x1e -#define MT9T031_READ_MODE_2 0x20 -#define MT9T031_READ_MODE_3 0x21 -#define MT9T031_ROW_ADDRESS_MODE 0x22 -#define MT9T031_COLUMN_ADDRESS_MODE 0x23 -#define MT9T031_GLOBAL_GAIN 0x35 -#define MT9T031_CHIP_ENABLE 0xF8 - -#define MT9T031_MAX_HEIGHT 1536 -#define MT9T031_MAX_WIDTH 2048 -#define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 18 -#define MT9T031_HORIZONTAL_BLANK 142 -#define MT9T031_VERTICAL_BLANK 25 -#define MT9T031_COLUMN_SKIP 32 -#define MT9T031_ROW_SKIP 20 - -struct mt9t031 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - u16 xskip; - u16 yskip; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9t031 *to_mt9t031(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int set_shutter(struct i2c_client *client, const u32 data) -{ - int ret; - - ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); - - if (ret >= 0) - ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); - - return ret; -} - -static int get_shutter(struct i2c_client *client, u32 *data) -{ - int ret; - - ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); - *data = ret << 16; - - if (ret >= 0) - ret = reg_read(client, MT9T031_SHUTTER_WIDTH); - *data |= ret & 0xffff; - - return ret < 0 ? ret : 0; -} - -static int mt9t031_idle(struct i2c_client *client) -{ - int ret; - - /* Disable chip output, synchronous option update */ - ret = reg_write(client, MT9T031_RESET, 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_RESET, 0); - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - return ret >= 0 ? 0 : -EIO; -} - -static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (enable) - /* Switch to master "normal" mode */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); - else - /* Stop sensor readout */ - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* target must be _even_ */ -static u16 mt9t031_skip(s32 *source, s32 target, s32 max) -{ - unsigned int skip; - - if (*source < target + target / 2) { - *source = target; - return 1; - } - - skip = min(max, *source + target / 2) / target; - if (skip > 8) - skip = 8; - *source = target * skip; - - return skip; -} - -/* rect is the sensor rectangle, the caller guarantees parameter validity */ -static int mt9t031_set_params(struct i2c_client *client, - struct v4l2_rect *rect, u16 xskip, u16 yskip) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - u16 xbin, ybin; - const u16 hblank = MT9T031_HORIZONTAL_BLANK, - vblank = MT9T031_VERTICAL_BLANK; - - xbin = min(xskip, (u16)3); - ybin = min(yskip, (u16)3); - - /* - * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. - * There is always a valid suitably aligned value. The worst case is - * xbin = 3, width = 2048. Then we will start at 36, the last read out - * pixel will be 2083, which is < 2085 - first black pixel. - * - * MT9T031 datasheet imposes window left border alignment, depending on - * the selected xskip. Failing to conform to this requirement produces - * dark horizontal stripes in the image. However, even obeying to this - * requirement doesn't eliminate the stripes in all configurations. They - * appear "locally reproducibly," but can differ between tests under - * different lighting conditions. - */ - switch (xbin) { - case 1: - rect->left &= ~1; - break; - case 2: - rect->left &= ~3; - break; - case 3: - rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? - (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); - } - - rect->top &= ~1; - - dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", - xskip, yskip, rect->width, rect->height, rect->left, rect->top); - - /* Disable register update, reconfigure atomically */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); - if (ret < 0) - return ret; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) - ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - - if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { - /* Binning, skipping */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - } - dev_dbg(&client->dev, "new physical left %u, top %u\n", - rect->left, rect->top); - - /* - * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() - */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, rect->left); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, rect->top); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { - mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; - - ret = set_shutter(client, mt9t031->total_h); - } - - /* Re-enable register update, commit all changes */ - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); - - if (ret >= 0) { - mt9t031->rect = *rect; - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } - - return ret < 0 ? ret : 0; -} - -static int mt9t031_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 mt9t031 *mt9t031 = to_mt9t031(client); - struct v4l2_rect rect = sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - - return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); -} - -static int mt9t031_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 mt9t031 *mt9t031 = to_mt9t031(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9T031_COLUMN_SKIP; - sel->r.top = MT9T031_ROW_SKIP; - sel->r.width = MT9T031_MAX_WIDTH; - sel->r.height = MT9T031_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9t031->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t031_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 mt9t031 *mt9t031 = to_mt9t031(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9t031->rect.width / mt9t031->xskip; - mf->height = mt9t031->rect.height / mt9t031->yskip; - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_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 mt9t031 *mt9t031 = to_mt9t031(client); - u16 xskip, yskip; - struct v4l2_rect rect = mt9t031->rect; - - if (format->pad) - return -EINVAL; - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Width and height are within limits. - * S_FMT: use binning and skipping for scaling - */ - xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); - yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - /* mt9t031_set_params() doesn't change width and height */ - return mt9t031_set_params(client, &rect, xskip, yskip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9t031_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9t031->exposure->minimum; - max = mt9t031->exposure->maximum; - mt9t031->exposure->val = - (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) - / shutter_max + min; - break; - } - return 0; -} - -static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - struct v4l2_subdev *sd = &mt9t031->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9t031->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 1015 + range / 2) / range + 9; - - if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ - data = gain; - else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ - data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - - dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", - reg_read(client, MT9T031_GLOBAL_GAIN), data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - u32 old; - - get_shutter(client, &old); - dev_dbg(&client->dev, "Set shutter from %u to %u\n", - old, shutter); - if (set_shutter(client, shutter) < 0) - return -EIO; - } else { - const u16 vblank = MT9T031_VERTICAL_BLANK; - mt9t031->total_h = mt9t031->rect.height + - mt9t031->y_skip_top + vblank; - - if (set_shutter(client, mt9t031->total_h) < 0) - return -EIO; - } - return 0; - default: - return -EINVAL; - } - return 0; -} - -/* - * Power Management: - * This function does nothing for now but must be present for pm to work - */ -static int mt9t031_runtime_suspend(struct device *dev) -{ - return 0; -} - -/* - * Power Management: - * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged - * they are however changed at reset if the platform hook is present - * thus we rewrite them with the values stored by the driver - */ -static int mt9t031_runtime_resume(struct device *dev) -{ - struct video_device *vdev = to_video_device(dev); - struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - int ret; - u16 xbin, ybin; - - xbin = min(mt9t031->xskip, (u16)3); - ybin = min(mt9t031->yskip, (u16)3); - - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (mt9t031->xskip - 1)); - if (ret < 0) - return ret; - - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (mt9t031->yskip - 1)); - if (ret < 0) - return ret; - - return 0; -} - -static const struct dev_pm_ops mt9t031_dev_pm_ops = { - .runtime_suspend = mt9t031_runtime_suspend, - .runtime_resume = mt9t031_runtime_resume, -}; - -static const struct device_type mt9t031_dev_type = { - .name = "MT9T031", - .pm = &mt9t031_dev_pm_ops, -}; - -static int mt9t031_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 video_device *vdev = soc_camera_i2c_to_vdev(client); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); - if (ret < 0) - return ret; - if (vdev) - /* Not needed during probing, when vdev isn't available yet */ - vdev->dev.type = &mt9t031_dev_type; - } else { - if (vdev) - vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); - } - - return 0; -} - -/* - * 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 mt9t031_video_probe(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - s32 data; - int ret; - - ret = mt9t031_s_power(&mt9t031->subdev, 1); - if (ret < 0) - return ret; - - ret = mt9t031_idle(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* Read out the chip version register */ - data = reg_read(client, MT9T031_CHIP_VERSION); - - switch (data) { - case 0x1621: - break; - default: - dev_err(&client->dev, - "No MT9T031 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - - ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); - -done: - mt9t031_s_power(&mt9t031->subdev, 0); - - return ret; -} - -static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - *lines = mt9t031->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { - .g_volatile_ctrl = mt9t031_g_volatile_ctrl, - .s_ctrl = mt9t031_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .s_power = mt9t031_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t031_g_register, - .s_register = mt9t031_s_register, -#endif -}; - -static int mt9t031_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) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int mt9t031_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_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t031_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); - - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_FALLING) - return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); -} - -static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { - .s_stream = mt9t031_s_stream, - .g_mbus_config = mt9t031_g_mbus_config, - .s_mbus_config = mt9t031_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { - .g_skip_top_lines = mt9t031_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { - .enum_mbus_code = mt9t031_enum_mbus_code, - .get_selection = mt9t031_get_selection, - .set_selection = mt9t031_set_selection, - .get_fmt = mt9t031_get_fmt, - .set_fmt = mt9t031_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9t031_subdev_ops = { - .core = &mt9t031_subdev_core_ops, - .video = &mt9t031_subdev_video_ops, - .sensor = &mt9t031_subdev_sensor_ops, - .pad = &mt9t031_subdev_pad_ops, -}; - -static int mt9t031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t031 *mt9t031; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); - if (!mt9t031) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); - v4l2_ctrl_handler_init(&mt9t031->hdl, 5); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, - &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) - return mt9t031->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - mt9t031->y_skip_top = 0; - mt9t031->rect.left = MT9T031_COLUMN_SKIP; - mt9t031->rect.top = MT9T031_ROW_SKIP; - mt9t031->rect.width = MT9T031_MAX_WIDTH; - mt9t031->rect.height = MT9T031_MAX_HEIGHT; - - mt9t031->xskip = 1; - mt9t031->yskip = 1; - - mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9t031->clk)) { - ret = PTR_ERR(mt9t031->clk); - goto eclkget; - } - - ret = mt9t031_video_probe(client); - if (ret) { - v4l2_clk_put(mt9t031->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9t031->hdl); - } - - return ret; -} - -static int mt9t031_remove(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - - v4l2_clk_put(mt9t031->clk); - v4l2_device_unregister_subdev(&mt9t031->subdev); - v4l2_ctrl_handler_free(&mt9t031->hdl); - - return 0; -} - -static const struct i2c_device_id mt9t031_id[] = { - { "mt9t031", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t031_id); - -static struct i2c_driver mt9t031_i2c_driver = { - .driver = { - .name = "mt9t031", - }, - .probe = mt9t031_probe, - .remove = mt9t031_remove, - .id_table = mt9t031_id, -}; - -module_i2c_driver(mt9t031_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc-camera.rst b/drivers/staging/media/soc_camera/soc-camera.rst deleted file mode 100644 index 7c39711aebf8..000000000000 --- a/drivers/staging/media/soc_camera/soc-camera.rst +++ /dev/null @@ -1,171 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -The Soc-Camera Drivers -====================== - -Author: Guennadi Liakhovetski - -Terminology ------------ - -The following terms are used in this document: - - camera / camera device / camera sensor - a video-camera sensor chip, capable - of connecting to a variety of systems and interfaces, typically uses i2c for - control and configuration, and a parallel or a serial bus for data. - - camera host - an interface, to which a camera is connected. Typically a - specialised interface, present on many SoCs, e.g. PXA27x and PXA3xx, SuperH, - i.MX27, i.MX31. - - camera host bus - a connection between a camera host and a camera. Can be - parallel or serial, consists of data and control lines, e.g. clock, vertical - and horizontal synchronization signals. - -Purpose of the soc-camera subsystem ------------------------------------ - -The soc-camera subsystem initially provided a unified API between camera host -drivers and camera sensor drivers. Later the soc-camera sensor API has been -replaced with the V4L2 standard subdev API. This also made camera driver re-use -with non-soc-camera hosts possible. The camera host API to the soc-camera core -has been preserved. - -Soc-camera implements a V4L2 interface to the user, currently only the "mmap" -method is supported by host drivers. However, the soc-camera core also provides -support for the "read" method. - -The subsystem has been designed to support multiple camera host interfaces and -multiple cameras per interface, although most applications have only one camera -sensor. - -Existing drivers ----------------- - -As of 3.7 there are seven host drivers in the mainline: atmel-isi.c, -mx1_camera.c (broken, scheduled for removal), mx2_camera.c, mx3_camera.c, -omap1_camera.c, pxa_camera.c, sh_mobile_ceu_camera.c, and multiple sensor -drivers under drivers/media/i2c/soc_camera/. - -Camera host API ---------------- - -A host camera driver is registered using the - -.. code-block:: none - - soc_camera_host_register(struct soc_camera_host *); - -function. The host object can be initialized as follows: - -.. code-block:: none - - struct soc_camera_host *ici; - ici->drv_name = DRV_NAME; - ici->ops = &camera_host_ops; - ici->priv = pcdev; - ici->v4l2_dev.dev = &pdev->dev; - ici->nr = pdev->id; - -All camera host methods are passed in a struct soc_camera_host_ops: - -.. code-block:: none - - static struct soc_camera_host_ops camera_host_ops = { - .owner = THIS_MODULE, - .add = camera_add_device, - .remove = camera_remove_device, - .set_fmt = camera_set_fmt_cap, - .try_fmt = camera_try_fmt_cap, - .init_videobuf2 = camera_init_videobuf2, - .poll = camera_poll, - .querycap = camera_querycap, - .set_bus_param = camera_set_bus_param, - /* The rest of host operations are optional */ - }; - -.add and .remove methods are called when a sensor is attached to or detached -from the host. .set_bus_param is used to configure physical connection -parameters between the host and the sensor. .init_videobuf2 is called by -soc-camera core when a video-device is opened, the host driver would typically -call vb2_queue_init() in this method. Further video-buffer management is -implemented completely by the specific camera host driver. If the host driver -supports non-standard pixel format conversion, it should implement a -.get_formats and, possibly, a .put_formats operations. See below for more -details about format conversion. The rest of the methods are called from -respective V4L2 operations. - -Camera API ----------- - -Sensor drivers can use struct soc_camera_link, typically provided by the -platform, and used to specify to which camera host bus the sensor is connected, -and optionally provide platform .power and .reset methods for the camera. This -struct is provided to the camera driver via the I2C client device platform data -and can be obtained, using the soc_camera_i2c_to_link() macro. Care should be -taken, when using soc_camera_vdev_to_subdev() and when accessing struct -soc_camera_device, using v4l2_get_subdev_hostdata(): both only work, when -running on an soc-camera host. The actual camera driver operation is implemented -using the V4L2 subdev API. Additionally soc-camera camera drivers can use -auxiliary soc-camera helper functions like soc_camera_power_on() and -soc_camera_power_off(), which switch regulators, provided by the platform and call -board-specific power switching methods. soc_camera_apply_board_flags() takes -camera bus configuration capability flags and applies any board transformations, -e.g. signal polarity inversion. soc_mbus_get_fmtdesc() can be used to obtain a -pixel format descriptor, corresponding to a certain media-bus pixel format code. -soc_camera_limit_side() can be used to restrict beginning and length of a frame -side, based on camera capabilities. - -VIDIOC_S_CROP and VIDIOC_S_FMT behaviour ----------------------------------------- - -Above user ioctls modify image geometry as follows: - -VIDIOC_S_CROP: sets location and sizes of the sensor window. Unit is one sensor -pixel. Changing sensor window sizes preserves any scaling factors, therefore -user window sizes change as well. - -VIDIOC_S_FMT: sets user window. Should preserve previously set sensor window as -much as possible by modifying scaling factors. If the sensor window cannot be -preserved precisely, it may be changed too. - -In soc-camera there are two locations, where scaling and cropping can take -place: in the camera driver and in the host driver. User ioctls are first passed -to the host driver, which then generally passes them down to the camera driver. -It is more efficient to perform scaling and cropping in the camera driver to -save camera bus bandwidth and maximise the framerate. However, if the camera -driver failed to set the required parameters with sufficient precision, the host -driver may decide to also use its own scaling and cropping to fulfill the user's -request. - -Camera drivers are interfaced to the soc-camera core and to host drivers over -the v4l2-subdev API, which is completely functional, it doesn't pass any data. -Therefore all camera drivers shall reply to .g_fmt() requests with their current -output geometry. This is necessary to correctly configure the camera bus. -.s_fmt() and .try_fmt() have to be implemented too. Sensor window and scaling -factors have to be maintained by camera drivers internally. According to the -V4L2 API all capture drivers must support the VIDIOC_CROPCAP ioctl, hence we -rely on camera drivers implementing .cropcap(). If the camera driver does not -support cropping, it may choose to not implement .s_crop(), but to enable -cropping support by the camera host driver at least the .g_crop method must be -implemented. - -User window geometry is kept in .user_width and .user_height fields in struct -soc_camera_device and used by the soc-camera core and host drivers. The core -updates these fields upon successful completion of a .s_fmt() call, but if these -fields change elsewhere, e.g. during .s_crop() processing, the host driver is -responsible for updating them. - -Format conversion ------------------ - -V4L2 distinguishes between pixel formats, as they are stored in memory, and as -they are transferred over a media bus. Soc-camera provides support to -conveniently manage these formats. A table of standard transformations is -maintained by soc-camera core, which describes, what FOURCC pixel format will -be obtained, if a media-bus pixel format is stored in memory according to -certain rules. E.g. if MEDIA_BUS_FMT_YUYV8_2X8 data is sampled with 8 bits per -sample and stored in memory in the little-endian order with no gaps between -bytes, data in memory will represent the V4L2_PIX_FMT_YUYV FOURCC format. These -standard transformations will be used by soc-camera or by camera host drivers to -configure camera drivers to produce the FOURCC format, requested by the user, -using the VIDIOC_S_FMT ioctl(). Apart from those standard format conversions, -host drivers can also provide their own conversion rules by implementing a -.get_formats and, if required, a .put_formats methods. diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c deleted file mode 100644 index 39f513f69b89..000000000000 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ /dev/null @@ -1,2164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * camera image capture (abstract) bus driver - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This driver provides an interface between platform-specific camera - * buses and camera devices. It should be used if the camera is - * connected not over a "proper" bus like PCI or USB, but over a - * special bus, like, for example, the Quick Capture interface on PXA270 - * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. - * It can handle multiple cameras and / or multiple buses, which can - * be used, e.g., in stereo-vision applications. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Default to VGA resolution */ -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - -#define MAP_MAX_NUM 32 -static DECLARE_BITMAP(device_map, MAP_MAX_NUM); -static LIST_HEAD(hosts); -static LIST_HEAD(devices); -/* - * Protects lists and bitmaps of hosts and devices. - * Lock nesting: Ok to take ->host_lock under list_lock. - */ -static DEFINE_MUTEX(list_lock); - -struct soc_camera_async_client { - struct v4l2_async_subdev *sensor; - struct v4l2_async_notifier notifier; - struct platform_device *pdev; - struct list_head list; /* needed for clean up */ -}; - -static int soc_camera_video_start(struct soc_camera_device *icd); -static int video_dev_create(struct soc_camera_device *icd); - -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret; - bool clock_toggle; - - if (clk && (!ssdd->unbalanced_power || - !test_and_set_bit(0, &ssdd->clock_state))) { - ret = v4l2_clk_enable(clk); - if (ret < 0) { - dev_err(dev, "Cannot enable clock: %d\n", ret); - return ret; - } - clock_toggle = true; - } else { - clock_toggle = false; - } - - ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) { - dev_err(dev, "Cannot enable regulators\n"); - goto eregenable; - } - - if (ssdd->power) { - ret = ssdd->power(dev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epwron; - } - } - - return 0; - -epwron: - regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -eregenable: - if (clock_toggle) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_on); - -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret = 0; - int err; - - if (ssdd->power) { - err = ssdd->power(dev, 0); - if (err < 0) { - dev_err(dev, - "Platform failed to power-off the camera.\n"); - ret = err; - } - } - - err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (err < 0) { - dev_err(dev, "Cannot disable regulators\n"); - ret = ret ? : err; - } - - if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_off); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) -{ - /* Should not have any effect in synchronous case */ - return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -} -EXPORT_SYMBOL(soc_camera_power_init); - -static int __soc_camera_power_on(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int __soc_camera_power_off(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int soc_camera_clock_start(struct soc_camera_host *ici) -{ - int ret; - - if (!ici->ops->clock_start) - return 0; - - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); - - return ret; -} - -static void soc_camera_clock_stop(struct soc_camera_host *ici) -{ - if (!ici->ops->clock_stop) - return; - - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); -} - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < icd->num_user_formats; i++) - if (icd->user_formats[i].host_fmt->fourcc == fourcc) - return icd->user_formats + i; - return NULL; -} -EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); - -/** - * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @ssdd: camera platform parameters - * @cfg: media bus configuration - * @return: resulting flags - */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg) -{ - unsigned long f, flags = cfg->flags; - - /* If only one of the two polarities is supported, switch to the opposite */ - if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); - if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) - flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - return flags; -} -EXPORT_SYMBOL(soc_camera_apply_board_flags); - -#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ - ((x) >> 24) & 0xff - -static int soc_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { - pix->bytesperline = 0; - pix->sizeimage = 0; - } - - ret = ici->ops->try_fmt(icd, f); - if (ret < 0) - return ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; - - ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); - if (ret < 0) - return ret; - - pix->bytesperline = max_t(u32, pix->bytesperline, ret); - - ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, - pix->height); - if (ret < 0) - return ret; - - pix->sizeimage = max_t(u32, pix->sizeimage, ret); - - return 0; -} - -static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - /* Only single-plane capture is supported so far */ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* limit format to hardware capabilities */ - return soc_camera_try_fmt(icd, f); -} - -static int soc_camera_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct soc_camera_device *icd = file->private_data; - - if (inp->index != 0) - return -EINVAL; - - /* default is camera */ - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = icd->vdev->tvnorms; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, s_std, a); -} - -static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, g_std, a); -} - -static int soc_camera_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->enum_framesizes(icd, fsize); -} - -static int soc_camera_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int ret; - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_reqbufs(&icd->vb2_vidq, p); - if (!ret) - icd->streamer = p->count ? file : NULL; - return ret; -} - -static int soc_camera_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - return vb2_querybuf(&icd->vb2_vidq, p); -} - -static int soc_camera_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_qbuf(&icd->vb2_vidq, NULL, p); -} - -static int soc_camera_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); -} - -static int soc_camera_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_create_bufs(&icd->vb2_vidq, create); - if (!ret) - icd->streamer = file; - return ret; -} - -static int soc_camera_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); -} - -static int soc_camera_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - return vb2_expbuf(&icd->vb2_vidq, p); -} - -/* Always entered with .host_lock held */ -static int soc_camera_init_user_formats(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - unsigned int i, fmts = 0, raw_fmts = 0; - int ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { - raw_fmts++; - code.index++; - } - - if (!ici->ops->get_formats) - /* - * Fallback mode - the host will have to serve all - * sensor-provided formats one-to-one to the user - */ - fmts = raw_fmts; - else - /* - * First pass - only count formats this host-sensor - * configuration can provide - */ - for (i = 0; i < raw_fmts; i++) { - ret = ici->ops->get_formats(icd, i, NULL); - if (ret < 0) - return ret; - fmts += ret; - } - - if (!fmts) - return -ENXIO; - - icd->user_formats = - vmalloc(array_size(fmts, - sizeof(struct soc_camera_format_xlate))); - if (!icd->user_formats) - return -ENOMEM; - - dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); - - /* Second pass - actually fill data formats */ - fmts = 0; - for (i = 0; i < raw_fmts; i++) - if (!ici->ops->get_formats) { - code.index = i; - v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - icd->user_formats[fmts].host_fmt = - soc_mbus_get_fmtdesc(code.code); - if (icd->user_formats[fmts].host_fmt) - icd->user_formats[fmts++].code = code.code; - } else { - ret = ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); - if (ret < 0) - goto egfmt; - fmts += ret; - } - - icd->num_user_formats = fmts; - icd->current_fmt = &icd->user_formats[0]; - - return 0; - -egfmt: - vfree(icd->user_formats); - return ret; -} - -/* Always entered with .host_lock held */ -static void soc_camera_free_user_formats(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->put_formats) - ici->ops->put_formats(icd); - icd->current_fmt = NULL; - icd->num_user_formats = 0; - vfree(icd->user_formats); - icd->user_formats = NULL; -} - -/* Called with .vb_lock held, or from the first open(2), see comment there */ -static int soc_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - /* We always call try_fmt() before set_fmt() or set_selection() */ - ret = soc_camera_try_fmt(icd, f); - if (ret < 0) - return ret; - - ret = ici->ops->set_fmt(icd, f); - if (ret < 0) { - return ret; - } else if (!icd->current_fmt || - icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { - dev_err(icd->pdev, - "Host driver hasn't set up current format correctly!\n"); - return -EINVAL; - } - - icd->user_width = pix->width; - icd->user_height = pix->height; - icd->bytesperline = pix->bytesperline; - icd->sizeimage = pix->sizeimage; - icd->colorspace = pix->colorspace; - icd->field = pix->field; - - dev_dbg(icd->pdev, "set width: %d height: %d\n", - icd->user_width, icd->user_height); - - /* set physical bus parameters */ - return ici->ops->set_bus_param(icd); -} - -static int soc_camera_add_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - if (ici->icd) - return -EBUSY; - - if (!icd->clk) { - ret = soc_camera_clock_start(ici); - if (ret < 0) - return ret; - } - - if (ici->ops->add) { - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - } - - ici->icd = icd; - - return 0; - -eadd: - if (!icd->clk) - soc_camera_clock_stop(ici); - return ret; -} - -static void soc_camera_remove_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (WARN_ON(icd != ici->icd)) - return; - - if (ici->ops->remove) - ici->ops->remove(icd); - if (!icd->clk) - soc_camera_clock_stop(ici); - ici->icd = NULL; -} - -static int soc_camera_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd; - struct soc_camera_host *ici; - int ret; - - /* - * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes. Also protect against a race with - * soc_camera_host_unregister(). - */ - if (mutex_lock_interruptible(&list_lock)) - return -ERESTARTSYS; - - if (!vdev || !video_is_registered(vdev)) { - mutex_unlock(&list_lock); - return -ENODEV; - } - - icd = video_get_drvdata(vdev); - ici = to_soc_camera_host(icd->parent); - - ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; - mutex_unlock(&list_lock); - - if (ret < 0) { - dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - return ret; - } - - if (!to_soc_camera_control(icd)) { - /* No device driver attached */ - ret = -ENODEV; - goto econtrol; - } - - if (mutex_lock_interruptible(&ici->host_lock)) { - ret = -ERESTARTSYS; - goto elockhost; - } - icd->use_count++; - - /* Now we really have to activate the camera */ - if (icd->use_count == 1) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - /* Restore parameters before the last close() per V4L2 API */ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = icd->user_width, - .height = icd->user_height, - .field = icd->field, - .colorspace = icd->colorspace, - .pixelformat = - icd->current_fmt->host_fmt->fourcc, - }, - }; - - /* The camera could have been already on, try to reset */ - if (sdesc->subdev_desc.reset) - if (icd->control) - sdesc->subdev_desc.reset(icd->control); - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - goto eiciadd; - } - - ret = __soc_camera_power_on(icd); - if (ret < 0) - goto epower; - - pm_runtime_enable(&icd->vdev->dev); - ret = pm_runtime_resume(&icd->vdev->dev); - if (ret < 0 && ret != -ENOSYS) - goto eresume; - - /* - * Try to configure with default parameters. Notice: this is the - * very first open, so, we cannot race against other calls, - * apart from someone else calling open() simultaneously, but - * .host_lock is protecting us against it. - */ - ret = soc_camera_set_fmt(icd, &f); - if (ret < 0) - goto esfmt; - - ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); - if (ret < 0) - goto einitvb; - v4l2_ctrl_handler_setup(&icd->ctrl_handler); - } - mutex_unlock(&ici->host_lock); - - file->private_data = icd; - dev_dbg(icd->pdev, "camera device open\n"); - - return 0; - - /* - * All errors are entered with the .host_lock held, first four also - * with use_count == 1 - */ -einitvb: -esfmt: - pm_runtime_disable(&icd->vdev->dev); -eresume: - __soc_camera_power_off(icd); -epower: - soc_camera_remove_device(icd); -eiciadd: - icd->use_count--; - mutex_unlock(&ici->host_lock); -elockhost: -econtrol: - module_put(ici->ops->owner); - - return ret; -} - -static int soc_camera_close(struct file *file) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - mutex_lock(&ici->host_lock); - if (icd->streamer == file) { - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); - icd->streamer = NULL; - } - icd->use_count--; - if (!icd->use_count) { - pm_runtime_suspend(&icd->vdev->dev); - pm_runtime_disable(&icd->vdev->dev); - - __soc_camera_power_off(icd); - - soc_camera_remove_device(icd); - } - - mutex_unlock(&ici->host_lock); - - module_put(ici->ops->owner); - - dev_dbg(icd->pdev, "camera device close\n"); - - return 0; -} - -static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - dev_dbg(icd->pdev, "read called, buf %p\n", buf); - - if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) - return vb2_read(&icd->vb2_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - - dev_err(icd->pdev, "camera device read not implemented\n"); - - return -EINVAL; -} - -static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int err; - - dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); - - if (icd->streamer != file) - return -EBUSY; - - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&ici->host_lock); - - dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, - err); - - return err; -} - -static __poll_t soc_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - __poll_t res = EPOLLERR; - - if (icd->streamer != file) - return EPOLLERR; - - mutex_lock(&ici->host_lock); - res = ici->ops->poll(file, pt); - mutex_unlock(&ici->host_lock); - return res; -} - -static const struct v4l2_file_operations soc_camera_fops = { - .owner = THIS_MODULE, - .open = soc_camera_open, - .release = soc_camera_close, - .unlocked_ioctl = video_ioctl2, - .read = soc_camera_read, - .mmap = soc_camera_mmap, - .poll = soc_camera_poll, -}; - -static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); - return -EINVAL; - } - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - if (vb2_is_streaming(&icd->vb2_vidq)) { - dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); - return -EBUSY; - } - - ret = soc_camera_set_fmt(icd, f); - - if (!ret && !icd->streamer) - icd->streamer = file; - - return ret; -} - -static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct soc_camera_device *icd = file->private_data; - const struct soc_mbus_pixelfmt *format; - - WARN_ON(priv != file->private_data); - - if (f->index >= icd->num_user_formats) - return -EINVAL; - - format = icd->user_formats[f->index].host_fmt; - - f->pixelformat = format->fourcc; - return 0; -} - -static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_pix_format *pix = &f->fmt.pix; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pix->width = icd->user_width; - pix->height = icd->user_height; - pix->bytesperline = icd->bytesperline; - pix->sizeimage = icd->sizeimage; - pix->field = icd->field; - pix->pixelformat = icd->current_fmt->host_fmt->fourcc; - pix->colorspace = icd->colorspace; - dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", - icd->current_fmt->host_fmt->fourcc); - return 0; -} - -static int soc_camera_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - WARN_ON(priv != file->private_data); - - strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); - return ici->ops->querycap(ici, cap); -} - -static int soc_camera_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* This calls buf_queue from host driver's videobuf2_queue_ops */ - ret = vb2_streamon(&icd->vb2_vidq, i); - if (!ret) - v4l2_subdev_call(sd, video, s_stream, 1); - - return ret; -} - -static int soc_camera_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* - * This calls buf_release from host driver's videobuf2_queue_ops for all - * remaining buffers. When the last buffer is freed, stop capture - */ - ret = vb2_streamoff(&icd->vb2_vidq, i); - - v4l2_subdev_call(sd, video, s_stream, 0); - - return ret; -} - -static int soc_camera_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - /* With a wrong type no need to try to fall back to cropping */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return ici->ops->get_selection(icd, s); -} - -static int soc_camera_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - /* In all these cases cropping emulation will not help */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - (s->target != V4L2_SEL_TGT_COMPOSE && - s->target != V4L2_SEL_TGT_CROP)) - return -EINVAL; - - if (s->target == V4L2_SEL_TGT_COMPOSE) { - /* No output size change during a running capture! */ - if (vb2_is_streaming(&icd->vb2_vidq) && - (icd->user_width != s->r.width || - icd->user_height != s->r.height)) - return -EBUSY; - - /* - * Only one user is allowed to change the output format, touch - * buffers, start / stop streaming, poll for data - */ - if (icd->streamer && icd->streamer != file) - return -EBUSY; - } - - if (s->target == V4L2_SEL_TGT_CROP && - vb2_is_streaming(&icd->vb2_vidq) && - ici->ops->set_liveselection) - ret = ici->ops->set_liveselection(icd, s); - else - ret = ici->ops->set_selection(icd, s); - if (!ret && - s->target == V4L2_SEL_TGT_COMPOSE) { - icd->user_width = s->r.width; - icd->user_height = s->r.height; - if (!icd->streamer) - icd->streamer = file; - } - - return ret; -} - -static int soc_camera_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->get_parm) - return ici->ops->get_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->set_parm) - return ici->ops->set_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd); - -/* So far this function cannot fail */ -static void scan_add_host(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd; - - mutex_lock(&list_lock); - - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - - /* The camera could have been already on, try to reset */ - if (ssdd->reset) - if (icd->control) - ssdd->reset(icd->control); - - icd->parent = ici->v4l2_dev.dev; - - /* Ignore errors */ - soc_camera_probe(ici, icd); - } - - mutex_unlock(&list_lock); -} - -/* - * It is invalid to call v4l2_clk_enable() after a successful probing - * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. - */ -static int soc_camera_clk_enable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return -ENODEV; - - ici = to_soc_camera_host(icd->parent); - - if (!try_module_get(ici->ops->owner)) - return -ENODEV; - - /* - * If a different client is currently being probed, the host will tell - * you to go - */ - return soc_camera_clock_start(ici); -} - -static void soc_camera_clk_disable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return; - - ici = to_soc_camera_host(icd->parent); - - soc_camera_clock_stop(ici); - - module_put(ici->ops->owner); -} - -/* - * Eventually, it would be more logical to make the respective host the clock - * owner, but then we would have to copy this struct for each ici. Besides, it - * would introduce the circular dependency problem, unless we port all client - * drivers to release the clock, when not in use. - */ -static const struct v4l2_clk_ops soc_camera_clk_ops = { - .owner = THIS_MODULE, - .enable = soc_camera_clk_enable, - .disable = soc_camera_clk_disable, -}; - -static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, - struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev; - int ret, i; - - mutex_lock(&list_lock); - i = find_first_zero_bit(device_map, MAP_MAX_NUM); - if (i < MAP_MAX_NUM) - set_bit(i, device_map); - mutex_unlock(&list_lock); - if (i >= MAP_MAX_NUM) - return -ENOMEM; - - pdev = platform_device_alloc("soc-camera-pdrv", i); - if (!pdev) - return -ENOMEM; - - ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); - if (ret < 0) { - platform_device_put(pdev); - return ret; - } - - sasc->pdev = pdev; - - return 0; -} - -static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev = sasc->pdev; - int ret; - - ret = platform_device_add(pdev); - if (ret < 0 || !pdev->dev.driver) - return NULL; - - return platform_get_drvdata(pdev); -} - -/* Locking: called with .host_lock held */ -static int soc_camera_probe_finish(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(sd, icd); - - v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); - - ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, - NULL, true); - if (ret < 0) - return ret; - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - return ret; - } - - /* At this point client .probe() should have run already */ - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eusrfmt; - - icd->field = V4L2_FIELD_ANY; - - ret = soc_camera_video_start(icd); - if (ret < 0) - goto evidstart; - - /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { - icd->user_width = mf->width; - icd->user_height = mf->height; - icd->colorspace = mf->colorspace; - icd->field = mf->field; - } - soc_camera_remove_device(icd); - - return 0; - -evidstart: - soc_camera_free_user_formats(icd); -eusrfmt: - soc_camera_remove_device(icd); - - return ret; -} - -#ifdef CONFIG_I2C_BOARDINFO -static int soc_camera_i2c_init(struct soc_camera_device *icd, - struct soc_camera_desc *sdesc) -{ - struct soc_camera_subdev_desc *ssdd; - struct i2c_client *client; - struct soc_camera_host *ici; - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct i2c_adapter *adap; - struct v4l2_subdev *subdev; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* First find out how we link the main client */ - if (icd->sasc) { - /* Async non-OF probing handled by the subdevice list */ - return -EPROBE_DEFER; - } - - ici = to_soc_camera_host(icd->parent); - adap = i2c_get_adapter(shd->i2c_adapter_id); - if (!adap) { - dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - shd->i2c_adapter_id); - return -ENODEV; - } - - ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); - if (!ssdd) { - ret = -ENOMEM; - goto ealloc; - } - /* - * In synchronous case we request regulators ourselves in - * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try - * to allocate them again. - */ - ssdd->sd_pdata.num_regulators = 0; - ssdd->sd_pdata.regulators = NULL; - shd->board_info->platform_data = ssdd; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - shd->i2c_adapter_id, shd->board_info->addr); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - shd->board_info, NULL); - if (!subdev) { - ret = -ENODEV; - goto ei2cnd; - } - - client = v4l2_get_subdevdata(subdev); - - /* Use to_i2c_client(dev) to recover the i2c client */ - icd->control = &client->dev; - - return 0; -ei2cnd: - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -eclkreg: - kfree(ssdd); -ealloc: - i2c_put_adapter(adap); - return ret; -} - -static void soc_camera_i2c_free(struct soc_camera_device *icd) -{ - struct i2c_client *client = - to_i2c_client(to_soc_camera_control(icd)); - struct i2c_adapter *adap; - struct soc_camera_subdev_desc *ssdd; - - icd->control = NULL; - if (icd->sasc) - return; - - adap = client->adapter; - ssdd = client->dev.platform_data; - v4l2_device_unregister_subdev(i2c_get_clientdata(client)); - i2c_unregister_device(client); - i2c_put_adapter(adap); - kfree(ssdd); - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -} - -/* - * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async - * internal global mutex, therefore cannot race against other asynchronous - * events. Until notifier->complete() (soc_camera_async_complete()) is called, - * the video device node is not registered and no V4L fops can occur. Unloading - * of the host driver also calls a v4l2-async function, so also there we're - * protected. - */ -static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (asd == sasc->sensor && !WARN_ON(icd->control)) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* - * Only now we get subdevice-specific information like - * regulators, flags, callbacks, etc. - */ - if (client) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = - soc_camera_i2c_to_desc(client); - if (ssdd) { - memcpy(&sdesc->subdev_desc, ssdd, - sizeof(sdesc->subdev_desc)); - if (ssdd->reset) - ssdd->reset(&client->dev); - } - - icd->control = &client->dev; - } - } - - return 0; -} - -static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - icd->control = NULL; - - if (icd->clk) { - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } -} - -static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (to_soc_camera_control(icd)) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - mutex_lock(&list_lock); - ret = soc_camera_probe(ici, icd); - mutex_unlock(&list_lock); - if (ret < 0) - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations soc_camera_async_ops = { - .bound = soc_camera_async_bound, - .unbind = soc_camera_async_unbind, - .complete = soc_camera_async_complete, -}; - -static int scan_async_group(struct soc_camera_host *ici, - struct v4l2_async_subdev **asd, unsigned int size) -{ - struct soc_camera_async_subdev *sasd; - struct soc_camera_async_client *sasc; - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - char clk_name[V4L2_CLK_NAME_SIZE]; - unsigned int i; - int ret; - - /* First look for a sensor */ - for (i = 0; i < size; i++) { - sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); - if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) - break; - } - - if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { - /* All useless */ - dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); - return -ENODEV; - } - - /* Or shall this be managed by the soc-camera device? */ - sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); - if (!sasc) - return -ENOMEM; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &sasd->asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - for (i = 0; i < size; i++) { - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); - if (ret) - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - sasd->asd.match.i2c.adapter_id, - sasd->asd.match.i2c.address); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_async_host(struct soc_camera_host *ici) -{ - struct v4l2_async_subdev **asd; - int j; - - for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { - scan_async_group(ici, asd, ici->asd_sizes[j]); - asd += ici->asd_sizes[j]; - } -} -#else -#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) -#define soc_camera_i2c_free(icd) do {} while (0) -#define scan_async_host(ici) do {} while (0) -#endif - -#ifdef CONFIG_OF - -struct soc_of_info { - struct soc_camera_async_subdev sasd; - struct soc_camera_async_client sasc; - struct v4l2_async_subdev *subdev; -}; - -static int soc_of_bind(struct soc_camera_host *ici, - struct device_node *ep, - struct device_node *remote) -{ - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - struct soc_camera_async_client *sasc; - struct soc_of_info *info; - struct i2c_client *client; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* allocate a new subdev and add match info to it */ - info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->sasd.asd.match.fwnode = of_fwnode_handle(remote); - info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - info->subdev = &info->sasd.asd; - - /* Or shall this be managed by the soc-camera device? */ - sasc = &info->sasc; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &info->sasd.asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); - if (ret) { - of_node_put(remote); - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - client = of_find_i2c_device_by_node(remote); - - if (client) - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - client->adapter->nr, client->addr); - else - v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, info); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_of_host(struct soc_camera_host *ici) -{ - struct device *dev = ici->v4l2_dev.dev; - struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; - unsigned int i; - - for (i = 0; ; i++) { - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - break; - - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "no remote for %pOF\n", epn); - continue; - } - - /* so we now have a remote node to connect */ - if (!i) - soc_of_bind(ici, epn, rem); - - if (i) { - dev_err(dev, "multiple subdevices aren't supported yet!\n"); - break; - } - } - - of_node_put(epn); -} - -#else -static inline void scan_of_host(struct soc_camera_host *ici) { } -#endif - -/* Called during host-driver probe */ -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct device *control = NULL; - int ret; - - dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); - - /* - * Currently the subdev with the largest number of controls (13) is - * ov6550. So let's pick 16 as a hint for the control handler. Note - * that this is a hint only: too large and you waste some memory, too - * small and there is a (very) small performance hit when looking up - * controls in the internal hash. - */ - ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); - if (ret < 0) - return ret; - - /* Must have icd->vdev before registering the device */ - ret = video_dev_create(icd); - if (ret < 0) - goto evdc; - - /* - * ..._video_start() will create a device node, video_register_device() - * itself is protected against concurrent open() calls, but we also have - * to protect our data also during client probing. - */ - - /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (shd->board_info) { - ret = soc_camera_i2c_init(icd, sdesc); - if (ret < 0 && ret != -EPROBE_DEFER) - goto eadd; - } else if (!shd->add_device || !shd->del_device) { - ret = -EINVAL; - goto eadd; - } else { - ret = soc_camera_clock_start(ici); - if (ret < 0) - goto eadd; - - if (shd->module_name) - ret = request_module(shd->module_name); - - ret = shd->add_device(icd); - if (ret < 0) - goto eadddev; - - /* - * FIXME: this is racy, have to use driver-binding notification, - * when it is available - */ - control = to_soc_camera_control(icd); - if (!control || !control->driver || !dev_get_drvdata(control) || - !try_module_get(control->driver->owner)) { - shd->del_device(icd); - ret = -ENODEV; - goto enodrv; - } - } - - mutex_lock(&ici->host_lock); - ret = soc_camera_probe_finish(icd); - mutex_unlock(&ici->host_lock); - if (ret < 0) - goto efinish; - - return 0; - -efinish: - if (shd->board_info) { - soc_camera_i2c_free(icd); - } else { - shd->del_device(icd); - module_put(control->driver->owner); -enodrv: -eadddev: - soc_camera_clock_stop(ici); - } -eadd: - if (icd->vdev) { - video_device_release(icd->vdev); - icd->vdev = NULL; - } -evdc: - v4l2_ctrl_handler_free(&icd->ctrl_handler); - return ret; -} - -/* - * This is called on device_unregister, which only means we have to disconnect - * from the host, but not remove ourselves from the device list. With - * asynchronous client probing this can also be called without - * soc_camera_probe_finish() having run. Careful with clean up. - */ -static int soc_camera_remove(struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct video_device *vdev = icd->vdev; - - v4l2_ctrl_handler_free(&icd->ctrl_handler); - if (vdev) { - video_unregister_device(vdev); - icd->vdev = NULL; - } - - if (sdesc->host_desc.board_info) { - soc_camera_i2c_free(icd); - } else { - struct device *dev = to_soc_camera_control(icd); - struct device_driver *drv = dev ? dev->driver : NULL; - if (drv) { - sdesc->host_desc.del_device(icd); - module_put(drv->owner); - } - } - - if (icd->num_user_formats) - soc_camera_free_user_formats(icd); - - if (icd->clk) { - /* For the synchronous case */ - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } - - if (icd->sasc) - platform_device_unregister(icd->sasc->pdev); - - return 0; -} - -static int default_g_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_s_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_g_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_g_parm_cap(icd->vdev, sd, a); -} - -static int default_s_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_s_parm_cap(icd->vdev, sd, a); -} - -static int default_enum_framesizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) -{ - int ret; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_subdev_frame_size_enum fse = { - .index = fsize->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); - if (!xlate) - return -EINVAL; - fse.code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); - if (ret < 0) - return ret; - - if (fse.min_width == fse.max_width && - fse.min_height == fse.max_height) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.min_width; - fsize->discrete.height = fse.min_height; - return 0; - } - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = fse.min_width; - fsize->stepwise.max_width = fse.max_width; - fsize->stepwise.min_height = fse.min_height; - fsize->stepwise.max_height = fse.max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - return 0; -} - -int soc_camera_host_register(struct soc_camera_host *ici) -{ - struct soc_camera_host *ix; - int ret; - - if (!ici || !ici->ops || - !ici->ops->try_fmt || - !ici->ops->set_fmt || - !ici->ops->set_bus_param || - !ici->ops->querycap || - !ici->ops->init_videobuf2 || - !ici->ops->poll || - !ici->v4l2_dev.dev) - return -EINVAL; - - if (!ici->ops->set_selection) - ici->ops->set_selection = default_s_selection; - if (!ici->ops->get_selection) - ici->ops->get_selection = default_g_selection; - if (!ici->ops->set_parm) - ici->ops->set_parm = default_s_parm; - if (!ici->ops->get_parm) - ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_framesizes) - ici->ops->enum_framesizes = default_enum_framesizes; - - mutex_lock(&list_lock); - list_for_each_entry(ix, &hosts, list) { - if (ix->nr == ici->nr) { - ret = -EBUSY; - goto edevreg; - } - } - - ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); - if (ret < 0) - goto edevreg; - - list_add_tail(&ici->list, &hosts); - mutex_unlock(&list_lock); - - mutex_init(&ici->host_lock); - mutex_init(&ici->clk_lock); - - if (ici->v4l2_dev.dev->of_node) - scan_of_host(ici); - else if (ici->asd_sizes) - /* - * No OF, host with a list of subdevices. Don't try to mix - * modes by initialising some groups statically and some - * dynamically! - */ - scan_async_host(ici); - else - /* Legacy: static platform devices from board data */ - scan_add_host(ici); - - return 0; - -edevreg: - mutex_unlock(&list_lock); - return ret; -} -EXPORT_SYMBOL(soc_camera_host_register); - -/* Unregister all clients! */ -void soc_camera_host_unregister(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd, *tmp; - struct soc_camera_async_client *sasc; - LIST_HEAD(notifiers); - - mutex_lock(&list_lock); - list_del(&ici->list); - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr && icd->sasc) { - /* as long as we hold the device, sasc won't be freed */ - get_device(icd->pdev); - list_add(&icd->sasc->list, ¬ifiers); - } - mutex_unlock(&list_lock); - - list_for_each_entry(sasc, ¬ifiers, list) { - /* Must call unlocked to avoid AB-BA dead-lock */ - v4l2_async_notifier_unregister(&sasc->notifier); - v4l2_async_notifier_cleanup(&sasc->notifier); - put_device(&sasc->pdev->dev); - } - - mutex_lock(&list_lock); - - list_for_each_entry_safe(icd, tmp, &devices, list) - if (icd->iface == ici->nr) - soc_camera_remove(icd); - - mutex_unlock(&list_lock); - - v4l2_device_unregister(&ici->v4l2_dev); -} -EXPORT_SYMBOL(soc_camera_host_unregister); - -/* Image capture device */ -static int soc_camera_device_register(struct soc_camera_device *icd) -{ - struct soc_camera_device *ix; - int num = -1, i; - - mutex_lock(&list_lock); - for (i = 0; i < 256 && num < 0; i++) { - num = i; - /* Check if this index is available on this interface */ - list_for_each_entry(ix, &devices, list) { - if (ix->iface == icd->iface && ix->devnum == i) { - num = -1; - break; - } - } - } - - if (num < 0) { - /* - * ok, we have 256 cameras on this host... - * man, stay reasonable... - */ - mutex_unlock(&list_lock); - return -ENOMEM; - } - - icd->devnum = num; - icd->use_count = 0; - icd->host_priv = NULL; - - /* - * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting - * it again - */ - i = to_platform_device(icd->pdev)->id; - if (i < 0) - /* One static (legacy) soc-camera platform device */ - i = 0; - if (i >= MAP_MAX_NUM) { - mutex_unlock(&list_lock); - return -EBUSY; - } - set_bit(i, device_map); - list_add_tail(&icd->list, &devices); - mutex_unlock(&list_lock); - - return 0; -} - -static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { - .vidioc_querycap = soc_camera_querycap, - .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, - .vidioc_enum_input = soc_camera_enum_input, - .vidioc_g_input = soc_camera_g_input, - .vidioc_s_input = soc_camera_s_input, - .vidioc_s_std = soc_camera_s_std, - .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_framesizes, - .vidioc_reqbufs = soc_camera_reqbufs, - .vidioc_querybuf = soc_camera_querybuf, - .vidioc_qbuf = soc_camera_qbuf, - .vidioc_dqbuf = soc_camera_dqbuf, - .vidioc_create_bufs = soc_camera_create_bufs, - .vidioc_prepare_buf = soc_camera_prepare_buf, - .vidioc_expbuf = soc_camera_expbuf, - .vidioc_streamon = soc_camera_streamon, - .vidioc_streamoff = soc_camera_streamoff, - .vidioc_g_selection = soc_camera_g_selection, - .vidioc_s_selection = soc_camera_s_selection, - .vidioc_g_parm = soc_camera_g_parm, - .vidioc_s_parm = soc_camera_s_parm, -}; - -static int video_dev_create(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct video_device *vdev = video_device_alloc(); - - if (!vdev) - return -ENOMEM; - - strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - - vdev->v4l2_dev = &ici->v4l2_dev; - vdev->fops = &soc_camera_fops; - vdev->ioctl_ops = &soc_camera_ioctl_ops; - vdev->release = video_device_release; - vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &ici->host_lock; - - icd->vdev = vdev; - - return 0; -} - -/* - * Called from soc_camera_probe() above with .host_lock held - */ -static int soc_camera_video_start(struct soc_camera_device *icd) -{ - const struct device_type *type = icd->vdev->dev.type; - int ret; - - if (!icd->parent) - return -ENODEV; - - video_set_drvdata(icd->vdev, icd); - if (icd->vdev->tvnorms == 0) { - /* disable the STD API if there are no tvnorms defined */ - v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); - } - ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - dev_err(icd->pdev, "video_register_device failed: %d\n", ret); - return ret; - } - - /* Restore device type, possibly set by the subdevice driver */ - icd->vdev->dev.type = type; - - return 0; -} - -static int soc_camera_pdrv_probe(struct platform_device *pdev) -{ - struct soc_camera_desc *sdesc = pdev->dev.platform_data; - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - struct soc_camera_device *icd; - int ret; - - if (!sdesc) - return -EINVAL; - - icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); - if (!icd) - return -ENOMEM; - - /* - * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below - * regulator allocation is a dummy. They are actually requested by the - * subdevice driver, using soc_camera_power_init(). Also note, that in - * that case regulators are attached to the I2C device and not to the - * camera platform device. - */ - ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) - return ret; - - icd->iface = sdesc->host_desc.bus_id; - icd->sdesc = sdesc; - icd->pdev = &pdev->dev; - platform_set_drvdata(pdev, icd); - - icd->user_width = DEFAULT_WIDTH; - icd->user_height = DEFAULT_HEIGHT; - - return soc_camera_device_register(icd); -} - -/* - * Only called on rmmod for each platform device, since they are not - * hot-pluggable. Now we know, that all our users - hosts and devices have - * been unloaded already - */ -static int soc_camera_pdrv_remove(struct platform_device *pdev) -{ - struct soc_camera_device *icd = platform_get_drvdata(pdev); - int i; - - if (!icd) - return -EINVAL; - - i = pdev->id; - if (i < 0) - i = 0; - - /* - * In synchronous mode with static platform devices this is called in a - * loop from drivers/base/dd.c::driver_detach(), no parallel execution, - * no need to lock. In asynchronous case the caller - - * soc_camera_host_unregister() - already holds the lock - */ - if (test_bit(i, device_map)) { - clear_bit(i, device_map); - list_del(&icd->list); - } - - return 0; -} - -static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = soc_camera_pdrv_remove, - .driver = { - .name = "soc-camera-pdrv", - }, -}; - -module_platform_driver(soc_camera_pdrv); - -MODULE_DESCRIPTION("Image capture bus driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/staging/media/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c deleted file mode 100644 index 2aa646c89c1f..000000000000 --- a/drivers/staging/media/soc_camera/soc_mediabus.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * soc-camera media bus helper routines - * - * Copyright (C) 2009, Guennadi Liakhovetski - */ -#include -#include - -#include -#include -#include - -static const struct soc_mbus_lookup mbus_fmt[] = { -{ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB666_1X18, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB666/32bpp", - .bits_per_sample = 18, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 24, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_BE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer 8 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Grey", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_Y10, - .name = "Grey 10bit", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_JPEG_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_JPEG, - .name = "JPEG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_VARIABLE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB444, - .name = "RGB444", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUV420, - .name = "YUYV 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVU420, - .name = "YVYU 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .name = "Bayer 8 GRBG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, - .name = "Bayer 10 BGGR DPCM 8", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .name = "Bayer 10 GBRG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .name = "Bayer 10 GRBG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .name = "Bayer 10 RGGB", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .name = "Bayer 12 BGGR", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .name = "Bayer 12 GBRG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .name = "Bayer 12 GRBG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .name = "Bayer 12 RGGB", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator) -{ - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - case SOC_MBUS_PACKING_EXTEND16: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_EXTEND32: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - *numerator = 2; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_1_5X8: - *numerator = 3; - *denominator = 2; - return 0; - case SOC_MBUS_PACKING_VARIABLE: - *numerator = 0; - *denominator = 1; - return 0; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_samples_per_pixel); - -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) -{ - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) - return width * mf->bits_per_sample / 8; - - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - return width * mf->bits_per_sample / 8; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - case SOC_MBUS_PACKING_EXTEND16: - return width * 2; - case SOC_MBUS_PACKING_1_5X8: - return width * 3 / 2; - case SOC_MBUS_PACKING_VARIABLE: - return 0; - case SOC_MBUS_PACKING_EXTEND32: - return width * 4; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_bytes_per_line); - -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height) -{ - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) - return bytes_per_line * height; - - switch (mf->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - return bytes_per_line * height * 2; - case SOC_MBUS_PACKING_1_5X8: - return bytes_per_line * height * 3 / 2; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(soc_mbus_image_size); - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n) -{ - int i; - - for (i = 0; i < n; i++) - if (lookup[i].code == code) - return &lookup[i].fmt; - - return NULL; -} -EXPORT_SYMBOL(soc_mbus_find_fmtdesc); - -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code) -{ - return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); -} -EXPORT_SYMBOL(soc_mbus_get_fmtdesc); - -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags) -{ - unsigned long common_flags; - bool hsync = true, vsync = true, pclk, data, mode; - bool mipi_lanes, mipi_clock; - - common_flags = cfg->flags & flags; - - switch (cfg->type) { - case V4L2_MBUS_PARALLEL: - hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW); - vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW); - /* fall through */ - case V4L2_MBUS_BT656: - pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); - data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW); - mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - return (!hsync || !vsync || !pclk || !data || !mode) ? - 0 : common_flags; - case V4L2_MBUS_CSI2_DPHY: - mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; - mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); - return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; - default: - WARN_ON(1); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(soc_mbus_config_compatible); - -static int __init soc_mbus_init(void) -{ - return 0; -} - -static void __exit soc_mbus_exit(void) -{ -} - -module_init(soc_mbus_init); -module_exit(soc_mbus_exit); - -MODULE_DESCRIPTION("soc-camera media bus interface"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c deleted file mode 100644 index 1739a618846d..000000000000 --- a/drivers/staging/media/soc_camera/soc_mt9v022.c +++ /dev/null @@ -1,1008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9V022 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -static char *sensor_type; -module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); - -/* mt9v022 selected register addresses */ -#define MT9V022_CHIP_VERSION 0x00 -#define MT9V022_COLUMN_START 0x01 -#define MT9V022_ROW_START 0x02 -#define MT9V022_WINDOW_HEIGHT 0x03 -#define MT9V022_WINDOW_WIDTH 0x04 -#define MT9V022_HORIZONTAL_BLANKING 0x05 -#define MT9V022_VERTICAL_BLANKING 0x06 -#define MT9V022_CHIP_CONTROL 0x07 -#define MT9V022_SHUTTER_WIDTH1 0x08 -#define MT9V022_SHUTTER_WIDTH2 0x09 -#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a -#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V022_RESET 0x0c -#define MT9V022_READ_MODE 0x0d -#define MT9V022_MONITOR_MODE 0x0e -#define MT9V022_PIXEL_OPERATION_MODE 0x0f -#define MT9V022_LED_OUT_CONTROL 0x1b -#define MT9V022_ADC_MODE_CONTROL 0x1c -#define MT9V022_REG32 0x20 -#define MT9V022_ANALOG_GAIN 0x35 -#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 -#define MT9V022_PIXCLK_FV_LV 0x74 -#define MT9V022_DIGITAL_TEST_PATTERN 0x7f -#define MT9V022_AEC_AGC_ENABLE 0xAF -#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD - -/* mt9v024 partial list register addresses changes with respect to mt9v022 */ -#define MT9V024_PIXCLK_FV_LV 0x72 -#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD - -/* Progressive scan, master, defaults */ -#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 - -#define MT9V022_MAX_WIDTH 752 -#define MT9V022_MAX_HEIGHT 480 -#define MT9V022_MIN_WIDTH 48 -#define MT9V022_MIN_HEIGHT 32 -#define MT9V022_COLUMN_SKIP 1 -#define MT9V022_ROW_SKIP 4 - -#define MT9V022_HORIZONTAL_BLANKING_MIN 43 -#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V022_HORIZONTAL_BLANKING_DEF 94 -#define MT9V022_VERTICAL_BLANKING_MIN 2 -#define MT9V022_VERTICAL_BLANKING_MAX 3000 -#define MT9V022_VERTICAL_BLANKING_DEF 45 - -#define is_mt9v022_rev3(id) (id == 0x1313) -#define is_mt9v024(id) (id == 0x1324) - -/* MT9V022 has only one fixed colorspace per pixelcode */ -struct mt9v022_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9v022_datafmt *mt9v022_find_datafmt( - u32 code, const struct mt9v022_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 mt9v022_datafmt mt9v022_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -/* only registers with different addresses on different mt9v02x sensors */ -struct mt9v02x_register { - u8 max_total_shutter_width; - u8 pixclk_fv_lv; -}; - -static const struct mt9v02x_register mt9v022_register = { - .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, -}; - -static const struct mt9v02x_register mt9v024_register = { - .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, -}; - -enum mt9v022_model { - MT9V022IX7ATM, - MT9V022IX7ATC, -}; - -struct mt9v022 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct { - /* gain/auto-gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9v022_datafmt *fmt; - const struct mt9v022_datafmt *fmts; - const struct mt9v02x_register *reg; - int num_fmts; - enum mt9v022_model model; - u16 chip_control; - u16 chip_version; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9v022 *to_mt9v022(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9v022_init(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - int ret; - - /* - * Almost the default mode: master, parallel, simultaneous, and an - * undocumented bit 0x200, which is present in table 7, but not in 8, - * plus snapshot mode to disable scan for now - */ - mt9v022->chip_control |= 0x10; - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (!ret) - ret = reg_write(client, MT9V022_READ_MODE, 0x300); - - /* All defaults */ - if (!ret) - /* AEC, AGC on */ - ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); - if (!ret) - ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); - if (!ret) - ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); - if (!ret) - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); - if (!ret) - /* default - auto */ - ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (!ret) - ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); - if (!ret) - return v4l2_ctrl_handler_setup(&mt9v022->hdl); - - return ret; -} - -static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (enable) { - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Unset snapshot mode specific settings: clear bit 9 - * and bit 2 in reg. 0x20 when in normal mode. - */ - if (reg_clear(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } else { - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Required settings for snapshot mode: set bit 9 - * (RST enable) and bit 2 (CR enable) in reg. 0x20 - * See TechNote TN0960 or TN-09-225. - */ - if (reg_set(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } - - if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) - return -EIO; - return 0; -} - -static int mt9v022_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 mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_rect rect = sel->r; - int min_row, min_blank; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* Bayer format - even size lengths */ - if (mt9v022->fmts == mt9v022_colour_fmts) { - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - /* Let the user play with the starting pixel */ - } - - soc_camera_limit_side(&rect.left, &rect.width, - MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); - - /* Like in example app. Contradicts the datasheet though */ - ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (ret >= 0) { - if (ret & 1) /* Autoexposure */ - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, - rect.height + mt9v022->y_skip_top + 43); - /* - * If autoexposure is off, there is no need to set - * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off - * only if the user has set exposure manually, using the - * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. - * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH - * already contains the correct value. - */ - } - /* Setup frame format: defaults apart from width and height */ - if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect.top); - /* - * mt9v022: min total row time is 660 columns, min blanking is 43 - * mt9v024: min total row time is 690 columns, min blanking is 61 - */ - if (is_mt9v024(mt9v022->chip_version)) { - min_row = 690; - min_blank = 61; - } else { - min_row = 660; - min_blank = 43; - } - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, - rect.width > min_row - min_blank ? - min_blank : min_row - rect.width); - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect.height + mt9v022->y_skip_top); - - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); - - mt9v022->rect = rect; - - return 0; -} - -static int mt9v022_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 mt9v022 *mt9v022 = to_mt9v022(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9V022_COLUMN_SKIP; - sel->r.top = MT9V022_ROW_SKIP; - sel->r.width = MT9V022_MAX_WIDTH; - sel->r.height = MT9V022_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9v022->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9v022_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 mt9v022 *mt9v022 = to_mt9v022(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mf->code = mt9v022->fmt->code; - mf->colorspace = mt9v022->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9v022_s_fmt(struct v4l2_subdev *sd, - const struct mt9v022_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9v022->rect.left, - .r.top = mt9v022->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* - * The caller provides a supported format, as verified per call to - * .set_fmt(FORMAT_TRY), datawidth is from our supported format list - */ - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_Y10_1X10: - if (mt9v022->model != MT9V022IX7ATM) - return -EINVAL; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: - if (mt9v022->model != MT9V022IX7ATC) - return -EINVAL; - break; - default: - return -EINVAL; - } - - /* No support for scaling on this camera, just crop. */ - ret = mt9v022_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mt9v022->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9v022_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 mt9v022 *mt9v022 = to_mt9v022(client); - const struct mt9v022_datafmt *fmt; - int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || - mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, - MT9V022_MAX_WIDTH, align, - &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, - MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); - - fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, - mt9v022->num_fmts); - if (!fmt) { - fmt = mt9v022->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9v022_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9v022_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9v022_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 mt9v022 *mt9v022 = to_mt9v022(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); -} - -static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *gain = mt9v022->gain; - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range; - int data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - data = reg_read(client, MT9V022_ANALOG_GAIN); - if (data < 0) - return -EIO; - - range = gain->maximum - gain->minimum; - gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; - return 0; - case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); - if (data < 0) - return -EIO; - - range = exp->maximum - exp->minimum; - exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; - return 0; - case V4L2_CID_HBLANK: - data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - case V4L2_CID_VBLANK: - data = reg_read(client, MT9V022_VERTICAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - } - return -EINVAL; -} - -static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->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, MT9V022_READ_MODE, 0x10); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x10); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x20); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x20); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_AUTOGAIN: - if (ctrl->val) { - if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - } else { - struct v4l2_ctrl *gain = mt9v022->gain; - /* mt9v022 has minimum == default */ - unsigned long range = gain->maximum - gain->minimum; - /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain_val = ((gain->val - (s32)gain->minimum) * - 48 + range / 2) / range + 16; - - if (gain_val >= 32) - gain_val &= ~1; - - /* - * The user wants to set gain manually, hope, she - * knows, what she's doing... Switch AGC off. - */ - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - - dev_dbg(&client->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain_val); - if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) - return -EIO; - } - return 0; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_AUTO) { - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); - } else { - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * - 479 + range / 2) / range + 1; - - /* - * The user wants to set shutter width manually, hope, - * she knows, what she's doing... Switch AEC off. - */ - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); - if (data < 0) - return -EIO; - dev_dbg(&client->dev, "Shutter width from %d to %lu\n", - reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), - shutter); - if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - shutter) < 0) - return -EIO; - } - return 0; - case V4L2_CID_HBLANK: - if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - case V4L2_CID_VBLANK: - if (reg_write(client, MT9V022_VERTICAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - } - return -EINVAL; -} - -/* - * 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 mt9v022_video_probe(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - s32 data; - int ret; - unsigned long flags; - - ret = mt9v022_s_power(&mt9v022->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data = reg_read(client, MT9V022_CHIP_VERSION); - - /* must be 0x1311, 0x1313 or 0x1324 */ - if (data != 0x1311 && data != 0x1313 && data != 0x1324) { - ret = -ENODEV; - dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", - data); - goto ei2c; - } - - mt9v022->chip_version = data; - - mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : - &mt9v022_register; - - /* Soft reset */ - ret = reg_write(client, MT9V022_RESET, 1); - if (ret < 0) - goto ei2c; - /* 15 clock cycles */ - udelay(200); - if (reg_read(client, MT9V022_RESET)) { - dev_err(&client->dev, "Resetting MT9V022 failed!\n"); - if (ret > 0) - ret = -EIO; - goto ei2c; - } - - /* Set monochrome or colour sensor type */ - if (sensor_type && (!strcmp("colour", sensor_type) || - !strcmp("color", sensor_type))) { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); - mt9v022->model = MT9V022IX7ATC; - mt9v022->fmts = mt9v022_colour_fmts; - } else { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); - mt9v022->model = MT9V022IX7ATM; - mt9v022->fmts = mt9v022_monochrome_fmts; - } - - if (ret < 0) - goto ei2c; - - mt9v022->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9v022->num_fmts++; - else - mt9v022->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9v022->num_fmts++; - - mt9v022->fmt = &mt9v022->fmts[0]; - - dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", - data, mt9v022->model == MT9V022IX7ATM ? - "monochrome" : "colour"); - - ret = mt9v022_init(client); - if (ret < 0) - dev_err(&client->dev, "Failed to initialise the camera\n"); - -ei2c: - mt9v022_s_power(&mt9v022->subdev, 0); - return ret; -} - -static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - *lines = mt9v022->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { - .g_volatile_ctrl = mt9v022_g_volatile_ctrl, - .s_ctrl = mt9v022_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v022_g_register, - .s_register = mt9v022_s_register, -#endif - .s_power = mt9v022_s_power, -}; - -static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (code->pad || code->index >= mt9v022->num_fmts) - return -EINVAL; - - code->code = mt9v022->fmts[code->index].code; - return 0; -} - -static int mt9v022_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_MASTER | V4L2_MBUS_SLAVE | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9v022_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); - struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; - int ret; - u16 pixclk = 0; - - if (ssdd->set_bus_param) { - ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - if (ret) - return ret; - } else if (bps != 10) { - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return -EINVAL; - } - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - pixclk |= 0x10; - - if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); - if (ret < 0) - return ret; - - if (!(flags & V4L2_MBUS_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { - .s_stream = mt9v022_s_stream, - .g_mbus_config = mt9v022_g_mbus_config, - .s_mbus_config = mt9v022_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { - .g_skip_top_lines = mt9v022_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { - .enum_mbus_code = mt9v022_enum_mbus_code, - .get_selection = mt9v022_get_selection, - .set_selection = mt9v022_set_selection, - .get_fmt = mt9v022_get_fmt, - .set_fmt = mt9v022_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9v022_subdev_ops = { - .core = &mt9v022_subdev_core_ops, - .video = &mt9v022_subdev_video_ops, - .sensor = &mt9v022_subdev_sensor_ops, - .pad = &mt9v022_subdev_pad_ops, -}; - -static int mt9v022_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v022 *mt9v022; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - struct mt9v022_platform_data *pdata; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9V022 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); - if (!mt9v022) - return -ENOMEM; - - pdata = ssdd->drv_priv; - v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); - v4l2_ctrl_handler_init(&mt9v022->hdl, 6); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, - &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, - MT9V022_HORIZONTAL_BLANKING_MAX, 1, - MT9V022_HORIZONTAL_BLANKING_DEF); - - mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, - MT9V022_VERTICAL_BLANKING_MAX, 1, - MT9V022_VERTICAL_BLANKING_DEF); - - mt9v022->subdev.ctrl_handler = &mt9v022->hdl; - if (mt9v022->hdl.error) { - int err = mt9v022->hdl.error; - - dev_err(&client->dev, "control initialisation err %d\n", err); - return err; - } - v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); - - mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - - /* - * On some platforms the first read out line is corrupted. - * Workaround it by skipping if indicated by platform data. - */ - mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; - mt9v022->rect.left = MT9V022_COLUMN_SKIP; - mt9v022->rect.top = MT9V022_ROW_SKIP; - mt9v022->rect.width = MT9V022_MAX_WIDTH; - mt9v022->rect.height = MT9V022_MAX_HEIGHT; - - mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9v022->clk)) { - ret = PTR_ERR(mt9v022->clk); - goto eclkget; - } - - ret = mt9v022_video_probe(client); - if (ret) { - v4l2_clk_put(mt9v022->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9v022->hdl); - } - - return ret; -} - -static int mt9v022_remove(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9v022->clk); - v4l2_device_unregister_subdev(&mt9v022->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&mt9v022->hdl); - - return 0; -} -static const struct i2c_device_id mt9v022_id[] = { - { "mt9v022", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v022_id); - -static struct i2c_driver mt9v022_i2c_driver = { - .driver = { - .name = "mt9v022", - }, - .probe = mt9v022_probe, - .remove = mt9v022_remove, - .id_table = mt9v022_id, -}; - -module_i2c_driver(mt9v022_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c deleted file mode 100644 index 39ae24dca65f..000000000000 --- a/drivers/staging/media/soc_camera/soc_ov5642.c +++ /dev/null @@ -1,1085 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for OV5642 CMOS Image Sensor from Omnivision - * - * Copyright (C) 2011, Bastian Hecht - * - * Based on Sony IMX074 Camera Driver - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Based on Omnivision OV7670 Camera Driver - * Copyright (C) 2006-7 Jonathan Corbet - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* OV5642 registers */ -#define REG_CHIP_ID_HIGH 0x300a -#define REG_CHIP_ID_LOW 0x300b - -#define REG_WINDOW_START_X_HIGH 0x3800 -#define REG_WINDOW_START_X_LOW 0x3801 -#define REG_WINDOW_START_Y_HIGH 0x3802 -#define REG_WINDOW_START_Y_LOW 0x3803 -#define REG_WINDOW_WIDTH_HIGH 0x3804 -#define REG_WINDOW_WIDTH_LOW 0x3805 -#define REG_WINDOW_HEIGHT_HIGH 0x3806 -#define REG_WINDOW_HEIGHT_LOW 0x3807 -#define REG_OUT_WIDTH_HIGH 0x3808 -#define REG_OUT_WIDTH_LOW 0x3809 -#define REG_OUT_HEIGHT_HIGH 0x380a -#define REG_OUT_HEIGHT_LOW 0x380b -#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c -#define REG_OUT_TOTAL_WIDTH_LOW 0x380d -#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e -#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f -#define REG_OUTPUT_FORMAT 0x4300 -#define REG_ISP_CTRL_01 0x5001 -#define REG_AVG_WINDOW_END_X_HIGH 0x5682 -#define REG_AVG_WINDOW_END_X_LOW 0x5683 -#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 -#define REG_AVG_WINDOW_END_Y_LOW 0x5687 - -/* active pixel array size */ -#define OV5642_SENSOR_SIZE_X 2592 -#define OV5642_SENSOR_SIZE_Y 1944 - -/* - * About OV5642 resolution, cropping and binning: - * This sensor supports it all, at least in the feature description. - * Unfortunately, no combination of appropriate registers settings could make - * the chip work the intended way. As it works with predefined register lists, - * some undocumented registers are presumably changed there to achieve their - * goals. - * This driver currently only works for resolutions up to 720 lines with a - * 1:1 scale. Hopefully these restrictions will be removed in the future. - */ -#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X -#define OV5642_MAX_HEIGHT 720 - -/* default sizes */ -#define OV5642_DEFAULT_WIDTH 1280 -#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT - -/* minimum extra blanking */ -#define BLANKING_EXTRA_WIDTH 500 -#define BLANKING_EXTRA_HEIGHT 20 - -/* - * the sensor's autoexposure is buggy when setting total_height low. - * It tries to expose longer than 1 frame period without taking care of it - * and this leads to weird output. So we set 1000 lines as minimum. - */ -#define BLANKING_MIN_HEIGHT 1000 - -struct regval_list { - u16 reg_num; - u8 value; -}; - -static struct regval_list ov5642_default_regs_init[] = { - { 0x3103, 0x93 }, - { 0x3008, 0x82 }, - { 0x3017, 0x7f }, - { 0x3018, 0xfc }, - { 0x3810, 0xc2 }, - { 0x3615, 0xf0 }, - { 0x3000, 0x0 }, - { 0x3001, 0x0 }, - { 0x3002, 0x0 }, - { 0x3003, 0x0 }, - { 0x3004, 0xff }, - { 0x3030, 0x2b }, - { 0x3011, 0x8 }, - { 0x3010, 0x10 }, - { 0x3604, 0x60 }, - { 0x3622, 0x60 }, - { 0x3621, 0x9 }, - { 0x3709, 0x0 }, - { 0x4000, 0x21 }, - { 0x401d, 0x22 }, - { 0x3600, 0x54 }, - { 0x3605, 0x4 }, - { 0x3606, 0x3f }, - { 0x3c01, 0x80 }, - { 0x300d, 0x22 }, - { 0x3623, 0x22 }, - { 0x5000, 0x4f }, - { 0x5020, 0x4 }, - { 0x5181, 0x79 }, - { 0x5182, 0x0 }, - { 0x5185, 0x22 }, - { 0x5197, 0x1 }, - { 0x5500, 0xa }, - { 0x5504, 0x0 }, - { 0x5505, 0x7f }, - { 0x5080, 0x8 }, - { 0x300e, 0x18 }, - { 0x4610, 0x0 }, - { 0x471d, 0x5 }, - { 0x4708, 0x6 }, - { 0x370c, 0xa0 }, - { 0x5687, 0x94 }, - { 0x501f, 0x0 }, - { 0x5000, 0x4f }, - { 0x5001, 0xcf }, - { 0x4300, 0x30 }, - { 0x4300, 0x30 }, - { 0x460b, 0x35 }, - { 0x471d, 0x0 }, - { 0x3002, 0xc }, - { 0x3002, 0x0 }, - { 0x4713, 0x3 }, - { 0x471c, 0x50 }, - { 0x4721, 0x2 }, - { 0x4402, 0x90 }, - { 0x460c, 0x22 }, - { 0x3815, 0x44 }, - { 0x3503, 0x7 }, - { 0x3501, 0x73 }, - { 0x3502, 0x80 }, - { 0x350b, 0x0 }, - { 0x3818, 0xc8 }, - { 0x3824, 0x11 }, - { 0x3a00, 0x78 }, - { 0x3a1a, 0x4 }, - { 0x3a13, 0x30 }, - { 0x3a18, 0x0 }, - { 0x3a19, 0x7c }, - { 0x3a08, 0x12 }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0xf }, - { 0x3a0b, 0xa0 }, - { 0x350c, 0x7 }, - { 0x350d, 0xd0 }, - { 0x3a0d, 0x8 }, - { 0x3a0e, 0x6 }, - { 0x3500, 0x0 }, - { 0x3501, 0x0 }, - { 0x3502, 0x0 }, - { 0x350a, 0x0 }, - { 0x350b, 0x0 }, - { 0x3503, 0x0 }, - { 0x3a0f, 0x3c }, - { 0x3a10, 0x32 }, - { 0x3a1b, 0x3c }, - { 0x3a1e, 0x32 }, - { 0x3a11, 0x80 }, - { 0x3a1f, 0x20 }, - { 0x3030, 0x2b }, - { 0x3a02, 0x0 }, - { 0x3a03, 0x7d }, - { 0x3a04, 0x0 }, - { 0x3a14, 0x0 }, - { 0x3a15, 0x7d }, - { 0x3a16, 0x0 }, - { 0x3a00, 0x78 }, - { 0x3a08, 0x9 }, - { 0x3a09, 0x60 }, - { 0x3a0a, 0x7 }, - { 0x3a0b, 0xd0 }, - { 0x3a0d, 0x10 }, - { 0x3a0e, 0xd }, - { 0x4407, 0x4 }, - { 0x5193, 0x70 }, - { 0x589b, 0x0 }, - { 0x589a, 0xc0 }, - { 0x401e, 0x20 }, - { 0x4001, 0x42 }, - { 0x401c, 0x6 }, - { 0x3825, 0xac }, - { 0x3827, 0xc }, - { 0x528a, 0x1 }, - { 0x528b, 0x4 }, - { 0x528c, 0x8 }, - { 0x528d, 0x10 }, - { 0x528e, 0x20 }, - { 0x528f, 0x28 }, - { 0x5290, 0x30 }, - { 0x5292, 0x0 }, - { 0x5293, 0x1 }, - { 0x5294, 0x0 }, - { 0x5295, 0x4 }, - { 0x5296, 0x0 }, - { 0x5297, 0x8 }, - { 0x5298, 0x0 }, - { 0x5299, 0x10 }, - { 0x529a, 0x0 }, - { 0x529b, 0x20 }, - { 0x529c, 0x0 }, - { 0x529d, 0x28 }, - { 0x529e, 0x0 }, - { 0x529f, 0x30 }, - { 0x5282, 0x0 }, - { 0x5300, 0x0 }, - { 0x5301, 0x20 }, - { 0x5302, 0x0 }, - { 0x5303, 0x7c }, - { 0x530c, 0x0 }, - { 0x530d, 0xc }, - { 0x530e, 0x20 }, - { 0x530f, 0x80 }, - { 0x5310, 0x20 }, - { 0x5311, 0x80 }, - { 0x5308, 0x20 }, - { 0x5309, 0x40 }, - { 0x5304, 0x0 }, - { 0x5305, 0x30 }, - { 0x5306, 0x0 }, - { 0x5307, 0x80 }, - { 0x5314, 0x8 }, - { 0x5315, 0x20 }, - { 0x5319, 0x30 }, - { 0x5316, 0x10 }, - { 0x5317, 0x0 }, - { 0x5318, 0x2 }, - { 0x5380, 0x1 }, - { 0x5381, 0x0 }, - { 0x5382, 0x0 }, - { 0x5383, 0x4e }, - { 0x5384, 0x0 }, - { 0x5385, 0xf }, - { 0x5386, 0x0 }, - { 0x5387, 0x0 }, - { 0x5388, 0x1 }, - { 0x5389, 0x15 }, - { 0x538a, 0x0 }, - { 0x538b, 0x31 }, - { 0x538c, 0x0 }, - { 0x538d, 0x0 }, - { 0x538e, 0x0 }, - { 0x538f, 0xf }, - { 0x5390, 0x0 }, - { 0x5391, 0xab }, - { 0x5392, 0x0 }, - { 0x5393, 0xa2 }, - { 0x5394, 0x8 }, - { 0x5480, 0x14 }, - { 0x5481, 0x21 }, - { 0x5482, 0x36 }, - { 0x5483, 0x57 }, - { 0x5484, 0x65 }, - { 0x5485, 0x71 }, - { 0x5486, 0x7d }, - { 0x5487, 0x87 }, - { 0x5488, 0x91 }, - { 0x5489, 0x9a }, - { 0x548a, 0xaa }, - { 0x548b, 0xb8 }, - { 0x548c, 0xcd }, - { 0x548d, 0xdd }, - { 0x548e, 0xea }, - { 0x548f, 0x1d }, - { 0x5490, 0x5 }, - { 0x5491, 0x0 }, - { 0x5492, 0x4 }, - { 0x5493, 0x20 }, - { 0x5494, 0x3 }, - { 0x5495, 0x60 }, - { 0x5496, 0x2 }, - { 0x5497, 0xb8 }, - { 0x5498, 0x2 }, - { 0x5499, 0x86 }, - { 0x549a, 0x2 }, - { 0x549b, 0x5b }, - { 0x549c, 0x2 }, - { 0x549d, 0x3b }, - { 0x549e, 0x2 }, - { 0x549f, 0x1c }, - { 0x54a0, 0x2 }, - { 0x54a1, 0x4 }, - { 0x54a2, 0x1 }, - { 0x54a3, 0xed }, - { 0x54a4, 0x1 }, - { 0x54a5, 0xc5 }, - { 0x54a6, 0x1 }, - { 0x54a7, 0xa5 }, - { 0x54a8, 0x1 }, - { 0x54a9, 0x6c }, - { 0x54aa, 0x1 }, - { 0x54ab, 0x41 }, - { 0x54ac, 0x1 }, - { 0x54ad, 0x20 }, - { 0x54ae, 0x0 }, - { 0x54af, 0x16 }, - { 0x54b0, 0x1 }, - { 0x54b1, 0x20 }, - { 0x54b2, 0x0 }, - { 0x54b3, 0x10 }, - { 0x54b4, 0x0 }, - { 0x54b5, 0xf0 }, - { 0x54b6, 0x0 }, - { 0x54b7, 0xdf }, - { 0x5402, 0x3f }, - { 0x5403, 0x0 }, - { 0x3406, 0x0 }, - { 0x5180, 0xff }, - { 0x5181, 0x52 }, - { 0x5182, 0x11 }, - { 0x5183, 0x14 }, - { 0x5184, 0x25 }, - { 0x5185, 0x24 }, - { 0x5186, 0x6 }, - { 0x5187, 0x8 }, - { 0x5188, 0x8 }, - { 0x5189, 0x7c }, - { 0x518a, 0x60 }, - { 0x518b, 0xb2 }, - { 0x518c, 0xb2 }, - { 0x518d, 0x44 }, - { 0x518e, 0x3d }, - { 0x518f, 0x58 }, - { 0x5190, 0x46 }, - { 0x5191, 0xf8 }, - { 0x5192, 0x4 }, - { 0x5193, 0x70 }, - { 0x5194, 0xf0 }, - { 0x5195, 0xf0 }, - { 0x5196, 0x3 }, - { 0x5197, 0x1 }, - { 0x5198, 0x4 }, - { 0x5199, 0x12 }, - { 0x519a, 0x4 }, - { 0x519b, 0x0 }, - { 0x519c, 0x6 }, - { 0x519d, 0x82 }, - { 0x519e, 0x0 }, - { 0x5025, 0x80 }, - { 0x3a0f, 0x38 }, - { 0x3a10, 0x30 }, - { 0x3a1b, 0x3a }, - { 0x3a1e, 0x2e }, - { 0x3a11, 0x60 }, - { 0x3a1f, 0x10 }, - { 0x5688, 0xa6 }, - { 0x5689, 0x6a }, - { 0x568a, 0xea }, - { 0x568b, 0xae }, - { 0x568c, 0xa6 }, - { 0x568d, 0x6a }, - { 0x568e, 0x62 }, - { 0x568f, 0x26 }, - { 0x5583, 0x40 }, - { 0x5584, 0x40 }, - { 0x5580, 0x2 }, - { 0x5000, 0xcf }, - { 0x5800, 0x27 }, - { 0x5801, 0x19 }, - { 0x5802, 0x12 }, - { 0x5803, 0xf }, - { 0x5804, 0x10 }, - { 0x5805, 0x15 }, - { 0x5806, 0x1e }, - { 0x5807, 0x2f }, - { 0x5808, 0x15 }, - { 0x5809, 0xd }, - { 0x580a, 0xa }, - { 0x580b, 0x9 }, - { 0x580c, 0xa }, - { 0x580d, 0xc }, - { 0x580e, 0x12 }, - { 0x580f, 0x19 }, - { 0x5810, 0xb }, - { 0x5811, 0x7 }, - { 0x5812, 0x4 }, - { 0x5813, 0x3 }, - { 0x5814, 0x3 }, - { 0x5815, 0x6 }, - { 0x5816, 0xa }, - { 0x5817, 0xf }, - { 0x5818, 0xa }, - { 0x5819, 0x5 }, - { 0x581a, 0x1 }, - { 0x581b, 0x0 }, - { 0x581c, 0x0 }, - { 0x581d, 0x3 }, - { 0x581e, 0x8 }, - { 0x581f, 0xc }, - { 0x5820, 0xa }, - { 0x5821, 0x5 }, - { 0x5822, 0x1 }, - { 0x5823, 0x0 }, - { 0x5824, 0x0 }, - { 0x5825, 0x3 }, - { 0x5826, 0x8 }, - { 0x5827, 0xc }, - { 0x5828, 0xe }, - { 0x5829, 0x8 }, - { 0x582a, 0x6 }, - { 0x582b, 0x4 }, - { 0x582c, 0x5 }, - { 0x582d, 0x7 }, - { 0x582e, 0xb }, - { 0x582f, 0x12 }, - { 0x5830, 0x18 }, - { 0x5831, 0x10 }, - { 0x5832, 0xc }, - { 0x5833, 0xa }, - { 0x5834, 0xb }, - { 0x5835, 0xe }, - { 0x5836, 0x15 }, - { 0x5837, 0x19 }, - { 0x5838, 0x32 }, - { 0x5839, 0x1f }, - { 0x583a, 0x18 }, - { 0x583b, 0x16 }, - { 0x583c, 0x17 }, - { 0x583d, 0x1e }, - { 0x583e, 0x26 }, - { 0x583f, 0x53 }, - { 0x5840, 0x10 }, - { 0x5841, 0xf }, - { 0x5842, 0xd }, - { 0x5843, 0xc }, - { 0x5844, 0xe }, - { 0x5845, 0x9 }, - { 0x5846, 0x11 }, - { 0x5847, 0x10 }, - { 0x5848, 0x10 }, - { 0x5849, 0x10 }, - { 0x584a, 0x10 }, - { 0x584b, 0xe }, - { 0x584c, 0x10 }, - { 0x584d, 0x10 }, - { 0x584e, 0x11 }, - { 0x584f, 0x10 }, - { 0x5850, 0xf }, - { 0x5851, 0xc }, - { 0x5852, 0xf }, - { 0x5853, 0x10 }, - { 0x5854, 0x10 }, - { 0x5855, 0xf }, - { 0x5856, 0xe }, - { 0x5857, 0xb }, - { 0x5858, 0x10 }, - { 0x5859, 0xd }, - { 0x585a, 0xd }, - { 0x585b, 0xc }, - { 0x585c, 0xc }, - { 0x585d, 0xc }, - { 0x585e, 0xb }, - { 0x585f, 0xc }, - { 0x5860, 0xc }, - { 0x5861, 0xc }, - { 0x5862, 0xd }, - { 0x5863, 0x8 }, - { 0x5864, 0x11 }, - { 0x5865, 0x18 }, - { 0x5866, 0x18 }, - { 0x5867, 0x19 }, - { 0x5868, 0x17 }, - { 0x5869, 0x19 }, - { 0x586a, 0x16 }, - { 0x586b, 0x13 }, - { 0x586c, 0x13 }, - { 0x586d, 0x12 }, - { 0x586e, 0x13 }, - { 0x586f, 0x16 }, - { 0x5870, 0x14 }, - { 0x5871, 0x12 }, - { 0x5872, 0x10 }, - { 0x5873, 0x11 }, - { 0x5874, 0x11 }, - { 0x5875, 0x16 }, - { 0x5876, 0x14 }, - { 0x5877, 0x11 }, - { 0x5878, 0x10 }, - { 0x5879, 0xf }, - { 0x587a, 0x10 }, - { 0x587b, 0x14 }, - { 0x587c, 0x13 }, - { 0x587d, 0x12 }, - { 0x587e, 0x11 }, - { 0x587f, 0x11 }, - { 0x5880, 0x12 }, - { 0x5881, 0x15 }, - { 0x5882, 0x14 }, - { 0x5883, 0x15 }, - { 0x5884, 0x15 }, - { 0x5885, 0x15 }, - { 0x5886, 0x13 }, - { 0x5887, 0x17 }, - { 0x3710, 0x10 }, - { 0x3632, 0x51 }, - { 0x3702, 0x10 }, - { 0x3703, 0xb2 }, - { 0x3704, 0x18 }, - { 0x370b, 0x40 }, - { 0x370d, 0x3 }, - { 0x3631, 0x1 }, - { 0x3632, 0x52 }, - { 0x3606, 0x24 }, - { 0x3620, 0x96 }, - { 0x5785, 0x7 }, - { 0x3a13, 0x30 }, - { 0x3600, 0x52 }, - { 0x3604, 0x48 }, - { 0x3606, 0x1b }, - { 0x370d, 0xb }, - { 0x370f, 0xc0 }, - { 0x3709, 0x1 }, - { 0x3823, 0x0 }, - { 0x5007, 0x0 }, - { 0x5009, 0x0 }, - { 0x5011, 0x0 }, - { 0x5013, 0x0 }, - { 0x519e, 0x0 }, - { 0x5086, 0x0 }, - { 0x5087, 0x0 }, - { 0x5088, 0x0 }, - { 0x5089, 0x0 }, - { 0x302b, 0x0 }, - { 0x3503, 0x7 }, - { 0x3011, 0x8 }, - { 0x350c, 0x2 }, - { 0x350d, 0xe4 }, - { 0x3621, 0xc9 }, - { 0x370a, 0x81 }, - { 0xffff, 0xff }, -}; - -static struct regval_list ov5642_default_regs_finalise[] = { - { 0x3810, 0xc2 }, - { 0x3818, 0xc9 }, - { 0x381c, 0x10 }, - { 0x381d, 0xa0 }, - { 0x381e, 0x5 }, - { 0x381f, 0xb0 }, - { 0x3820, 0x0 }, - { 0x3821, 0x0 }, - { 0x3824, 0x11 }, - { 0x3a08, 0x1b }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0x17 }, - { 0x3a0b, 0x20 }, - { 0x3a0d, 0x2 }, - { 0x3a0e, 0x1 }, - { 0x401c, 0x4 }, - { 0x5682, 0x5 }, - { 0x5683, 0x0 }, - { 0x5686, 0x2 }, - { 0x5687, 0xcc }, - { 0x5001, 0x4f }, - { 0x589b, 0x6 }, - { 0x589a, 0xc5 }, - { 0x3503, 0x0 }, - { 0x460c, 0x20 }, - { 0x460b, 0x37 }, - { 0x471c, 0xd0 }, - { 0x471d, 0x5 }, - { 0x3815, 0x1 }, - { 0x3818, 0xc1 }, - { 0x501f, 0x0 }, - { 0x5002, 0xe0 }, - { 0x4300, 0x32 }, /* UYVY */ - { 0x3002, 0x1c }, - { 0x4800, 0x14 }, - { 0x4801, 0xf }, - { 0x3007, 0x3b }, - { 0x300e, 0x4 }, - { 0x4803, 0x50 }, - { 0x3815, 0x1 }, - { 0x4713, 0x2 }, - { 0x4842, 0x1 }, - { 0x300f, 0xe }, - { 0x3003, 0x3 }, - { 0x3003, 0x1 }, - { 0xffff, 0xff }, -}; - -struct ov5642_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct ov5642 { - struct v4l2_subdev subdev; - const struct ov5642_datafmt *fmt; - struct v4l2_rect crop_rect; - struct v4l2_clk *clk; - - /* blanking information */ - int total_width; - int total_height; -}; - -static const struct ov5642_datafmt ov5642_colour_fmts[] = { - {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, -}; - -static struct ov5642 *to_ov5642(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ov5642, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct ov5642_datafmt - *ov5642_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) - if (ov5642_colour_fmts[i].code == code) - return ov5642_colour_fmts + i; - - return NULL; -} - -static int reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - /* We have 16-bit i2c addresses - care for endianness */ - unsigned char data[2] = { reg >> 8, reg & 0xff }; - - ret = i2c_master_send(client, data, 2); - if (ret < 2) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - ret = i2c_master_recv(client, val, 1); - if (ret < 1) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - return 0; -} - -static int reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - int ret; - unsigned char data[3] = { reg >> 8, reg & 0xff, val }; - - ret = i2c_master_send(client, data, 3); - if (ret < 3) { - dev_err(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - return 0; -} - -/* - * convenience function to write 16 bit register values that are split up - * into two consecutive high and low parts - */ -static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) -{ - int ret; - - ret = reg_write(client, reg, val16 >> 8); - if (ret) - return ret; - return reg_write(client, reg + 1, val16 & 0x00ff); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov5642_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 1; - - ret = reg_read(client, reg->reg, &val); - if (!ret) - reg->val = (__u64)val; - - return ret; -} - -static int ov5642_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov5642_write_array(struct i2c_client *client, - struct regval_list *vals) -{ - while (vals->reg_num != 0xffff || vals->value != 0xff) { - int ret = reg_write(client, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - dev_dbg(&client->dev, "Register list loaded\n"); - return 0; -} - -static int ov5642_set_resolution(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - int width = priv->crop_rect.width; - int height = priv->crop_rect.height; - int total_width = priv->total_width; - int total_height = priv->total_height; - int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; - int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; - int ret; - - /* - * This should set the starting point for cropping. - * Doesn't work so far. - */ - ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); - if (!ret) - ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); - if (!ret) { - priv->crop_rect.left = start_x; - priv->crop_rect.top = start_y; - } - - if (!ret) - ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); - if (ret) - return ret; - priv->crop_rect.width = width; - priv->crop_rect.height = height; - - /* Set the output window size. Only 1:1 scale is supported so far. */ - ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); - - /* Total width = output size + blanking */ - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); - - /* Sets the window for AWB calculations */ - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); - - return ret; -} - -static int ov5642_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 ov5642 *priv = to_ov5642(client); - const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); - - if (format->pad) - return -EINVAL; - - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - - if (!fmt) { - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = ov5642_colour_fmts[0].code; - mf->colorspace = ov5642_colour_fmts[0].colorspace; - } - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - return 0; -} - -static int ov5642_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 ov5642 *priv = to_ov5642(client); - - const struct ov5642_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov5642_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(ov5642_colour_fmts)) - return -EINVAL; - - code->code = ov5642_colour_fmts[code->index].code; - return 0; -} - -static int ov5642_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 ov5642 *priv = to_ov5642(client); - struct v4l2_rect rect = sel->r; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, - &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect.height + - BLANKING_EXTRA_HEIGHT, - BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static int ov5642_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 ov5642 *priv = to_ov5642(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV5642_MAX_WIDTH; - sel->r.height = OV5642_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->crop_rect; - return 0; - default: - return -EINVAL; - } -} - -static int ov5642_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static int ov5642_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 ov5642 *priv = to_ov5642(client); - int ret; - - if (!on) - return soc_camera_power_off(&client->dev, ssdd, priv->clk); - - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .g_mbus_config = ov5642_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { - .enum_mbus_code = ov5642_enum_mbus_code, - .get_selection = ov5642_get_selection, - .set_selection = ov5642_set_selection, - .get_fmt = ov5642_get_fmt, - .set_fmt = ov5642_set_fmt, -}; - -static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { - .s_power = ov5642_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov5642_get_register, - .s_register = ov5642_set_register, -#endif -}; - -static const struct v4l2_subdev_ops ov5642_subdev_ops = { - .core = &ov5642_subdev_core_ops, - .video = &ov5642_subdev_video_ops, - .pad = &ov5642_subdev_pad_ops, -}; - -static int ov5642_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u8 id_high, id_low; - u16 id; - - ret = ov5642_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); - if (ret < 0) - goto done; - - id = id_high << 8; - - ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); - if (ret < 0) - goto done; - - id |= id_low; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x5642) { - ret = -ENODEV; - goto done; - } - - ret = 0; - -done: - ov5642_s_power(subdev, 0); - return ret; -} - -static int ov5642_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov5642 *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "OV5642: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - - priv->fmt = &ov5642_colour_fmts[0]; - - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; - priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; - priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; - priv->total_height = BLANKING_MIN_HEIGHT; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = ov5642_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int ov5642_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - - v4l2_clk_put(priv->clk); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id ov5642_id[] = { - { "ov5642", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov5642_id); - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id ov5642_of_match[] = { - { .compatible = "ovti,ov5642" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ov5642_of_match); -#endif - -static struct i2c_driver ov5642_i2c_driver = { - .driver = { - .name = "ov5642", - .of_match_table = of_match_ptr(ov5642_of_match), - }, - .probe = ov5642_probe, - .remove = ov5642_remove, - .id_table = ov5642_id, -}; - -module_i2c_driver(ov5642_i2c_driver); - -MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); -MODULE_AUTHOR("Bastian Hecht "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c deleted file mode 100644 index 7c765595d85f..000000000000 --- a/drivers/staging/media/soc_camera/soc_ov9740.c +++ /dev/null @@ -1,992 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * OmniVision OV9740 Camera Driver - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Based on ov9640 camera driver. - */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) - -/* General Status Registers */ -#define OV9740_MODEL_ID_HI 0x0000 -#define OV9740_MODEL_ID_LO 0x0001 -#define OV9740_REVISION_NUMBER 0x0002 -#define OV9740_MANUFACTURER_ID 0x0003 -#define OV9740_SMIA_VERSION 0x0004 - -/* General Setup Registers */ -#define OV9740_MODE_SELECT 0x0100 -#define OV9740_IMAGE_ORT 0x0101 -#define OV9740_SOFTWARE_RESET 0x0103 -#define OV9740_GRP_PARAM_HOLD 0x0104 -#define OV9740_MSK_CORRUP_FM 0x0105 - -/* Timing Setting */ -#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ -#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ -#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ -#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ -#define OV9740_X_ADDR_START_HI 0x0344 -#define OV9740_X_ADDR_START_LO 0x0345 -#define OV9740_Y_ADDR_START_HI 0x0346 -#define OV9740_Y_ADDR_START_LO 0x0347 -#define OV9740_X_ADDR_END_HI 0x0348 -#define OV9740_X_ADDR_END_LO 0x0349 -#define OV9740_Y_ADDR_END_HI 0x034a -#define OV9740_Y_ADDR_END_LO 0x034b -#define OV9740_X_OUTPUT_SIZE_HI 0x034c -#define OV9740_X_OUTPUT_SIZE_LO 0x034d -#define OV9740_Y_OUTPUT_SIZE_HI 0x034e -#define OV9740_Y_OUTPUT_SIZE_LO 0x034f - -/* IO Control Registers */ -#define OV9740_IO_CREL00 0x3002 -#define OV9740_IO_CREL01 0x3004 -#define OV9740_IO_CREL02 0x3005 -#define OV9740_IO_OUTPUT_SEL01 0x3026 -#define OV9740_IO_OUTPUT_SEL02 0x3027 - -/* AWB Registers */ -#define OV9740_AWB_MANUAL_CTRL 0x3406 - -/* Analog Control Registers */ -#define OV9740_ANALOG_CTRL01 0x3601 -#define OV9740_ANALOG_CTRL02 0x3602 -#define OV9740_ANALOG_CTRL03 0x3603 -#define OV9740_ANALOG_CTRL04 0x3604 -#define OV9740_ANALOG_CTRL10 0x3610 -#define OV9740_ANALOG_CTRL12 0x3612 -#define OV9740_ANALOG_CTRL15 0x3615 -#define OV9740_ANALOG_CTRL20 0x3620 -#define OV9740_ANALOG_CTRL21 0x3621 -#define OV9740_ANALOG_CTRL22 0x3622 -#define OV9740_ANALOG_CTRL30 0x3630 -#define OV9740_ANALOG_CTRL31 0x3631 -#define OV9740_ANALOG_CTRL32 0x3632 -#define OV9740_ANALOG_CTRL33 0x3633 - -/* Sensor Control */ -#define OV9740_SENSOR_CTRL03 0x3703 -#define OV9740_SENSOR_CTRL04 0x3704 -#define OV9740_SENSOR_CTRL05 0x3705 -#define OV9740_SENSOR_CTRL07 0x3707 - -/* Timing Control */ -#define OV9740_TIMING_CTRL17 0x3817 -#define OV9740_TIMING_CTRL19 0x3819 -#define OV9740_TIMING_CTRL33 0x3833 -#define OV9740_TIMING_CTRL35 0x3835 - -/* Banding Filter */ -#define OV9740_AEC_MAXEXPO_60_H 0x3a02 -#define OV9740_AEC_MAXEXPO_60_L 0x3a03 -#define OV9740_AEC_B50_STEP_HI 0x3a08 -#define OV9740_AEC_B50_STEP_LO 0x3a09 -#define OV9740_AEC_B60_STEP_HI 0x3a0a -#define OV9740_AEC_B60_STEP_LO 0x3a0b -#define OV9740_AEC_CTRL0D 0x3a0d -#define OV9740_AEC_CTRL0E 0x3a0e -#define OV9740_AEC_MAXEXPO_50_H 0x3a14 -#define OV9740_AEC_MAXEXPO_50_L 0x3a15 - -/* AEC/AGC Control */ -#define OV9740_AEC_ENABLE 0x3503 -#define OV9740_GAIN_CEILING_01 0x3a18 -#define OV9740_GAIN_CEILING_02 0x3a19 -#define OV9740_AEC_HI_THRESHOLD 0x3a11 -#define OV9740_AEC_3A1A 0x3a1a -#define OV9740_AEC_CTRL1B_WPT2 0x3a1b -#define OV9740_AEC_CTRL0F_WPT 0x3a0f -#define OV9740_AEC_CTRL10_BPT 0x3a10 -#define OV9740_AEC_CTRL1E_BPT2 0x3a1e -#define OV9740_AEC_LO_THRESHOLD 0x3a1f - -/* BLC Control */ -#define OV9740_BLC_AUTO_ENABLE 0x4002 -#define OV9740_BLC_MODE 0x4005 - -/* VFIFO */ -#define OV9740_VFIFO_READ_START_HI 0x4608 -#define OV9740_VFIFO_READ_START_LO 0x4609 - -/* DVP Control */ -#define OV9740_DVP_VSYNC_CTRL02 0x4702 -#define OV9740_DVP_VSYNC_MODE 0x4704 -#define OV9740_DVP_VSYNC_CTRL06 0x4706 - -/* PLL Setting */ -#define OV9740_PLL_MODE_CTRL01 0x3104 -#define OV9740_PRE_PLL_CLK_DIV 0x0305 -#define OV9740_PLL_MULTIPLIER 0x0307 -#define OV9740_VT_SYS_CLK_DIV 0x0303 -#define OV9740_VT_PIX_CLK_DIV 0x0301 -#define OV9740_PLL_CTRL3010 0x3010 -#define OV9740_VFIFO_CTRL00 0x460e - -/* ISP Control */ -#define OV9740_ISP_CTRL00 0x5000 -#define OV9740_ISP_CTRL01 0x5001 -#define OV9740_ISP_CTRL03 0x5003 -#define OV9740_ISP_CTRL05 0x5005 -#define OV9740_ISP_CTRL12 0x5012 -#define OV9740_ISP_CTRL19 0x5019 -#define OV9740_ISP_CTRL1A 0x501a -#define OV9740_ISP_CTRL1E 0x501e -#define OV9740_ISP_CTRL1F 0x501f -#define OV9740_ISP_CTRL20 0x5020 -#define OV9740_ISP_CTRL21 0x5021 - -/* AWB */ -#define OV9740_AWB_CTRL00 0x5180 -#define OV9740_AWB_CTRL01 0x5181 -#define OV9740_AWB_CTRL02 0x5182 -#define OV9740_AWB_CTRL03 0x5183 -#define OV9740_AWB_ADV_CTRL01 0x5184 -#define OV9740_AWB_ADV_CTRL02 0x5185 -#define OV9740_AWB_ADV_CTRL03 0x5186 -#define OV9740_AWB_ADV_CTRL04 0x5187 -#define OV9740_AWB_ADV_CTRL05 0x5188 -#define OV9740_AWB_ADV_CTRL06 0x5189 -#define OV9740_AWB_ADV_CTRL07 0x518a -#define OV9740_AWB_ADV_CTRL08 0x518b -#define OV9740_AWB_ADV_CTRL09 0x518c -#define OV9740_AWB_ADV_CTRL10 0x518d -#define OV9740_AWB_ADV_CTRL11 0x518e -#define OV9740_AWB_CTRL0F 0x518f -#define OV9740_AWB_CTRL10 0x5190 -#define OV9740_AWB_CTRL11 0x5191 -#define OV9740_AWB_CTRL12 0x5192 -#define OV9740_AWB_CTRL13 0x5193 -#define OV9740_AWB_CTRL14 0x5194 - -/* MIPI Control */ -#define OV9740_MIPI_CTRL00 0x4800 -#define OV9740_MIPI_3837 0x3837 -#define OV9740_MIPI_CTRL01 0x4801 -#define OV9740_MIPI_CTRL03 0x4803 -#define OV9740_MIPI_CTRL05 0x4805 -#define OV9740_VFIFO_RD_CTRL 0x4601 -#define OV9740_MIPI_CTRL_3012 0x3012 -#define OV9740_SC_CMMM_MIPI_CTR 0x3014 - -#define OV9740_MAX_WIDTH 1280 -#define OV9740_MAX_HEIGHT 720 - -/* Misc. structures */ -struct ov9740_reg { - u16 reg; - u8 val; -}; - -struct ov9740_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - u16 model; - u8 revision; - u8 manid; - u8 smiaver; - - bool flag_vflip; - bool flag_hflip; - - /* For suspend/resume. */ - struct v4l2_mbus_framefmt current_mf; - bool current_enable; -}; - -static const struct ov9740_reg ov9740_defaults[] = { - /* Software Reset */ - { OV9740_SOFTWARE_RESET, 0x01 }, - - /* Banding Filter */ - { OV9740_AEC_B50_STEP_HI, 0x00 }, - { OV9740_AEC_B50_STEP_LO, 0xe8 }, - { OV9740_AEC_CTRL0E, 0x03 }, - { OV9740_AEC_MAXEXPO_50_H, 0x15 }, - { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, - { OV9740_AEC_B60_STEP_HI, 0x00 }, - { OV9740_AEC_B60_STEP_LO, 0xc0 }, - { OV9740_AEC_CTRL0D, 0x04 }, - { OV9740_AEC_MAXEXPO_60_H, 0x18 }, - { OV9740_AEC_MAXEXPO_60_L, 0x20 }, - - /* LC */ - { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, - { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, - - /* Un-documented OV9740 registers */ - { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, - { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, - { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, - { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, - { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, - { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, - { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, - { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, - { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, - { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, - { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, - { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, - { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, - { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, - { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, - { 0x583c, 0x5f }, - - /* Y Gamma */ - { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, - { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, - { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, - { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, - - /* UV Gamma */ - { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, - { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, - { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, - { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, - { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, - { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, - { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, - { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, - - /* AWB */ - { OV9740_AWB_CTRL00, 0xf0 }, - { OV9740_AWB_CTRL01, 0x00 }, - { OV9740_AWB_CTRL02, 0x41 }, - { OV9740_AWB_CTRL03, 0x42 }, - { OV9740_AWB_ADV_CTRL01, 0x8a }, - { OV9740_AWB_ADV_CTRL02, 0x61 }, - { OV9740_AWB_ADV_CTRL03, 0xce }, - { OV9740_AWB_ADV_CTRL04, 0xa8 }, - { OV9740_AWB_ADV_CTRL05, 0x17 }, - { OV9740_AWB_ADV_CTRL06, 0x1f }, - { OV9740_AWB_ADV_CTRL07, 0x27 }, - { OV9740_AWB_ADV_CTRL08, 0x41 }, - { OV9740_AWB_ADV_CTRL09, 0x34 }, - { OV9740_AWB_ADV_CTRL10, 0xf0 }, - { OV9740_AWB_ADV_CTRL11, 0x10 }, - { OV9740_AWB_CTRL0F, 0xff }, - { OV9740_AWB_CTRL10, 0x00 }, - { OV9740_AWB_CTRL11, 0xff }, - { OV9740_AWB_CTRL12, 0x00 }, - { OV9740_AWB_CTRL13, 0xff }, - { OV9740_AWB_CTRL14, 0x00 }, - - /* CIP */ - { 0x530d, 0x12 }, - - /* CMX */ - { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, - { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, - { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, - { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, - { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, - { 0x5394, 0x18 }, - - /* 50/60 Detection */ - { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, - - /* Output Select */ - { OV9740_IO_OUTPUT_SEL01, 0x00 }, - { OV9740_IO_OUTPUT_SEL02, 0x00 }, - { OV9740_IO_CREL00, 0x00 }, - { OV9740_IO_CREL01, 0x00 }, - { OV9740_IO_CREL02, 0x00 }, - - /* AWB Control */ - { OV9740_AWB_MANUAL_CTRL, 0x00 }, - - /* Analog Control */ - { OV9740_ANALOG_CTRL03, 0xaa }, - { OV9740_ANALOG_CTRL32, 0x2f }, - { OV9740_ANALOG_CTRL20, 0x66 }, - { OV9740_ANALOG_CTRL21, 0xc0 }, - { OV9740_ANALOG_CTRL31, 0x52 }, - { OV9740_ANALOG_CTRL33, 0x50 }, - { OV9740_ANALOG_CTRL30, 0xca }, - { OV9740_ANALOG_CTRL04, 0x0c }, - { OV9740_ANALOG_CTRL01, 0x40 }, - { OV9740_ANALOG_CTRL02, 0x16 }, - { OV9740_ANALOG_CTRL10, 0xa1 }, - { OV9740_ANALOG_CTRL12, 0x24 }, - { OV9740_ANALOG_CTRL22, 0x9f }, - { OV9740_ANALOG_CTRL15, 0xf0 }, - - /* Sensor Control */ - { OV9740_SENSOR_CTRL03, 0x42 }, - { OV9740_SENSOR_CTRL04, 0x10 }, - { OV9740_SENSOR_CTRL05, 0x45 }, - { OV9740_SENSOR_CTRL07, 0x14 }, - - /* Timing Control */ - { OV9740_TIMING_CTRL33, 0x04 }, - { OV9740_TIMING_CTRL35, 0x02 }, - { OV9740_TIMING_CTRL19, 0x6e }, - { OV9740_TIMING_CTRL17, 0x94 }, - - /* AEC/AGC Control */ - { OV9740_AEC_ENABLE, 0x10 }, - { OV9740_GAIN_CEILING_01, 0x00 }, - { OV9740_GAIN_CEILING_02, 0x7f }, - { OV9740_AEC_HI_THRESHOLD, 0xa0 }, - { OV9740_AEC_3A1A, 0x05 }, - { OV9740_AEC_CTRL1B_WPT2, 0x50 }, - { OV9740_AEC_CTRL0F_WPT, 0x50 }, - { OV9740_AEC_CTRL10_BPT, 0x4c }, - { OV9740_AEC_CTRL1E_BPT2, 0x4c }, - { OV9740_AEC_LO_THRESHOLD, 0x26 }, - - /* BLC Control */ - { OV9740_BLC_AUTO_ENABLE, 0x45 }, - { OV9740_BLC_MODE, 0x18 }, - - /* DVP Control */ - { OV9740_DVP_VSYNC_CTRL02, 0x04 }, - { OV9740_DVP_VSYNC_MODE, 0x00 }, - { OV9740_DVP_VSYNC_CTRL06, 0x08 }, - - /* PLL Setting */ - { OV9740_PLL_MODE_CTRL01, 0x20 }, - { OV9740_PRE_PLL_CLK_DIV, 0x03 }, - { OV9740_PLL_MULTIPLIER, 0x4c }, - { OV9740_VT_SYS_CLK_DIV, 0x01 }, - { OV9740_VT_PIX_CLK_DIV, 0x08 }, - { OV9740_PLL_CTRL3010, 0x01 }, - { OV9740_VFIFO_CTRL00, 0x82 }, - - /* Timing Setting */ - /* VTS */ - { OV9740_FRM_LENGTH_LN_HI, 0x03 }, - { OV9740_FRM_LENGTH_LN_LO, 0x07 }, - /* HTS */ - { OV9740_LN_LENGTH_PCK_HI, 0x06 }, - { OV9740_LN_LENGTH_PCK_LO, 0x62 }, - - /* MIPI Control */ - { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ - { OV9740_MIPI_3837, 0x01 }, - { OV9740_MIPI_CTRL01, 0x0f }, - { OV9740_MIPI_CTRL03, 0x05 }, - { OV9740_MIPI_CTRL05, 0x10 }, - { OV9740_VFIFO_RD_CTRL, 0x16 }, - { OV9740_MIPI_CTRL_3012, 0x70 }, - { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, - - /* YUYV order */ - { OV9740_ISP_CTRL19, 0x02 }, -}; - -static u32 ov9740_codes[] = { - MEDIA_BUS_FMT_YUYV8_2X8, -}; - -/* read a register */ -static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 1, - .buf = val, - }, - }; - - reg = swab16(reg); - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) { - dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - -/* write a register */ -static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - struct i2c_msg msg; - struct { - u16 reg; - u8 val; - } __packed buf; - int ret; - - reg = swab16(reg); - - buf.reg = reg; - buf.val = val; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 3; - msg.buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9740_reg_read(client, reg, &val); - if (ret < 0) { - dev_err(&client->dev, - "[Read]-Modify-Write of register 0x%04x failed!\n", - reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9740_reg_write(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, - "Read-Modify-[Write] of register 0x%04x failed!\n", - reg); - return ret; - } - - return 0; -} - -static int ov9740_reg_write_array(struct i2c_client *client, - const struct ov9740_reg *regarray, - int regarraylen) -{ - int i; - int ret; - - for (i = 0; i < regarraylen; i++) { - ret = ov9740_reg_write(client, - regarray[i].reg, regarray[i].val); - if (ret < 0) - return ret; - } - - return 0; -} - -/* Start/Stop streaming from the device */ -static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - /* Program orientation register. */ - if (priv->flag_vflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); - if (ret < 0) - return ret; - - if (priv->flag_hflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); - if (ret < 0) - return ret; - - if (enable) { - dev_dbg(&client->dev, "Enabling Streaming\n"); - /* Start Streaming */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); - - } else { - dev_dbg(&client->dev, "Disabling Streaming\n"); - /* Software Reset */ - ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); - if (!ret) - /* Setting Streaming to Standby */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, - 0x00); - } - - priv->current_enable = enable; - - return ret; -} - -/* select nearest higher resolution for capture */ -static void ov9740_res_roundup(u32 *width, u32 *height) -{ - /* Width must be a multiple of 4 pixels. */ - *width = ALIGN(*width, 4); - - /* Max resolution is 1280x720 (720p). */ - if (*width > OV9740_MAX_WIDTH) - *width = OV9740_MAX_WIDTH; - - if (*height > OV9740_MAX_HEIGHT) - *height = OV9740_MAX_HEIGHT; -} - -/* Setup registers according to resolution and color encoding */ -static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) -{ - u32 x_start; - u32 y_start; - u32 x_end; - u32 y_end; - bool scaling = false; - u32 scale_input_x; - u32 scale_input_y; - int ret; - - if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) - scaling = true; - - /* - * Try to use as much of the sensor area as possible when supporting - * smaller resolutions. Depending on the aspect ratio of the - * chosen resolution, we can either use the full width of the sensor, - * or the full height of the sensor (or both if the aspect ratio is - * the same as 1280x720. - */ - if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { - scale_input_x = (OV9740_MAX_HEIGHT * width) / height; - scale_input_y = OV9740_MAX_HEIGHT; - } else { - scale_input_x = OV9740_MAX_WIDTH; - scale_input_y = (OV9740_MAX_WIDTH * height) / width; - } - - /* These describe the area of the sensor to use. */ - x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; - y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; - x_end = x_start + scale_input_x - 1; - y_end = y_start + scale_input_y - 1; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, - (scale_input_x - width) >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, - (scale_input_x - width) & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | - (scaling << 4)); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); - -done: - return ret; -} - -/* set the format we will capture in */ -static int ov9740_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - ret = ov9740_reg_write_array(client, ov9740_defaults, - ARRAY_SIZE(ov9740_defaults)); - if (ret < 0) - return ret; - - ret = ov9740_set_res(client, mf->width, mf->height); - if (ret < 0) - return ret; - - priv->current_mf = *mf; - return ret; -} - -static int ov9740_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9740_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9740_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int ov9740_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(ov9740_codes)) - return -EINVAL; - - code->code = ov9740_codes[code->index]; - - return 0; -} - -static int ov9740_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV9740_MAX_WIDTH; - sel->r.height = OV9740_MAX_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -/* Set status of additional camera capabilities */ -static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9740_priv *priv = - container_of(ctrl->handler, struct ov9740_priv, hdl); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->val; - break; - case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ov9740_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 ov9740_priv *priv = to_ov9740(sd); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - if (priv->current_enable) { - ov9740_s_fmt(sd, &priv->current_mf); - ov9740_s_stream(sd, 1); - } - } else { - if (priv->current_enable) { - ov9740_s_stream(sd, 0); - priv->current_enable = true; - } - - soc_camera_power_off(&client->dev, ssdd, priv->clk); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9740_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 2; - - ret = ov9740_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return ret; -} - -static int ov9740_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return ov9740_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9740_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9740_priv *priv = to_ov9740(sd); - u8 modelhi, modello; - int ret; - - ret = ov9740_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); - if (ret < 0) - goto done; - - priv->model = (modelhi << 8) | modello; - - ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); - if (ret < 0) - goto done; - - if (priv->model != 0x9740) { - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", - priv->model, priv->revision, priv->manid, priv->smiaver); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9740_s_power(&priv->subdev, 0); - return ret; -} - -/* Request bus settings on camera side */ -static int ov9740_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_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9740_video_ops = { - .s_stream = ov9740_s_stream, - .g_mbus_config = ov9740_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops ov9740_core_ops = { - .s_power = ov9740_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9740_get_register, - .s_register = ov9740_set_register, -#endif -}; - -static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { - .enum_mbus_code = ov9740_enum_mbus_code, - .get_selection = ov9740_get_selection, - .set_fmt = ov9740_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9740_subdev_ops = { - .core = &ov9740_core_ops, - .video = &ov9740_video_ops, - .pad = &ov9740_pad_ops, -}; - -static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { - .s_ctrl = ov9740_s_ctrl, -}; - -/* - * i2c_driver function - */ -static int ov9740_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9740_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 13); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9740_video_probe(client); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9740_remove(struct i2c_client *client) -{ - struct ov9740_priv *priv = i2c_get_clientdata(client); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9740_id[] = { - { "ov9740", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9740_id); - -static struct i2c_driver ov9740_i2c_driver = { - .driver = { - .name = "ov9740", - }, - .probe = ov9740_probe, - .remove = ov9740_remove, - .id_table = ov9740_id, -}; - -module_i2c_driver(ov9740_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); -MODULE_AUTHOR("Andrew Chew "); -MODULE_LICENSE("GPL v2"); diff --git a/include/media/drv-intf/soc_mediabus.h b/include/media/drv-intf/soc_mediabus.h deleted file mode 100644 index 361f8852c9fc..000000000000 --- a/include/media/drv-intf/soc_mediabus.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * SoC-camera Media Bus API extensions - * - * Copyright (C) 2009, Guennadi Liakhovetski - */ - -#ifndef SOC_MEDIABUS_H -#define SOC_MEDIABUS_H - -#include -#include - -/** - * enum soc_mbus_packing - data packing types on the media-bus - * @SOC_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one - * sample represents one pixel - * @SOC_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the - * possibly incomplete byte high bits are padding - * @SOC_MBUS_PACKING_2X8_PADLO: as above, but low bits are padding - * @SOC_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended - * to 16 bits - * @SOC_MBUS_PACKING_VARIABLE: compressed formats with variable packing - * @SOC_MBUS_PACKING_1_5X8: used for packed YUV 4:2:0 formats, where 4 - * pixels occupy 6 bytes in RAM - * @SOC_MBUS_PACKING_EXTEND32: sample width (e.g., 24 bits) has to be extended - * to 32 bits - */ -enum soc_mbus_packing { - SOC_MBUS_PACKING_NONE, - SOC_MBUS_PACKING_2X8_PADHI, - SOC_MBUS_PACKING_2X8_PADLO, - SOC_MBUS_PACKING_EXTEND16, - SOC_MBUS_PACKING_VARIABLE, - SOC_MBUS_PACKING_1_5X8, - SOC_MBUS_PACKING_EXTEND32, -}; - -/** - * enum soc_mbus_order - sample order on the media bus - * @SOC_MBUS_ORDER_LE: least significant sample first - * @SOC_MBUS_ORDER_BE: most significant sample first - */ -enum soc_mbus_order { - SOC_MBUS_ORDER_LE, - SOC_MBUS_ORDER_BE, -}; - -/** - * enum soc_mbus_layout - planes layout in memory - * @SOC_MBUS_LAYOUT_PACKED: color components packed - * @SOC_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) - * @SOC_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a - * chroma plane (C plane is half the size - * of Y plane) - * @SOC_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a - * chroma plane (C plane is the same size - * as Y plane) - */ -enum soc_mbus_layout { - SOC_MBUS_LAYOUT_PACKED = 0, - SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, - SOC_MBUS_LAYOUT_PLANAR_2Y_C, - SOC_MBUS_LAYOUT_PLANAR_Y_C, -}; - -/** - * struct soc_mbus_pixelfmt - Data format on the media bus - * @fourcc: Fourcc code, that will be obtained if the data is - * stored in memory in the following way: - * @packing: Type of sample-packing, that has to be used - * @order: Sample order when storing in memory - * @bits_per_sample: How many bits the bridge has to sample - */ -struct soc_mbus_pixelfmt { - u32 fourcc; - enum soc_mbus_packing packing; - enum soc_mbus_order order; - enum soc_mbus_layout layout; - u8 bits_per_sample; -}; - -/** - * struct soc_mbus_lookup - Lookup FOURCC IDs by mediabus codes for pass-through - * @code: mediabus pixel-code - * @fmt: pixel format description - */ -struct soc_mbus_lookup { - u32 code; - struct soc_mbus_pixelfmt fmt; -}; - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n); -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code); -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height); -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator); -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags); - -#endif -- cgit v1.2.3 From d93d45ab716e4107056be54969c8c70e50a8346d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 19 Jun 2020 16:30:07 +0200 Subject: media: allegro: Fix some NULL vs IS_ERR() checks in probe The devm_ioremap() function doesn't return error pointers, it returns NULL on error. Fixes: f20387dfd065 ("media: allegro: add Allegro DVT video IP core driver") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 70f133a842dd..3ed66aae741d 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -3065,9 +3065,9 @@ static int allegro_probe(struct platform_device *pdev) return -EINVAL; } regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(regs)) { + if (!regs) { dev_err(&pdev->dev, "failed to map registers\n"); - return PTR_ERR(regs); + return -ENOMEM; } dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &allegro_regmap_config); @@ -3085,9 +3085,9 @@ static int allegro_probe(struct platform_device *pdev) sram_regs = devm_ioremap(&pdev->dev, sram_res->start, resource_size(sram_res)); - if (IS_ERR(sram_regs)) { + if (!sram_regs) { dev_err(&pdev->dev, "failed to map sram\n"); - return PTR_ERR(sram_regs); + return -ENOMEM; } dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, &allegro_sram_config); -- cgit v1.2.3 From 7b8ce1f2763b9351a4cb04b802835470e76770a5 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:15 +0200 Subject: media: staging: rkisp1: rsz: supported formats are the isp's src formats, not sink formats The rkisp1_resizer's enum callback 'rkisp1_rsz_enum_mbus_code' calls the enum callback of the 'rkisp1_isp' on it's video sink pad. This is a bug, the resizer should support the same formats supported by the 'rkisp1_isp' on the source pad (not the sink pad). Fixes: 56e3b29f9f6b "media: staging: rkisp1: add streaming paths" Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 26fb41053f56..27f4b948ad41 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -437,8 +437,8 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, u32 pad = code->pad; int ret; - /* supported mbus codes are the same in isp sink pad */ - code->pad = RKISP1_ISP_PAD_SINK_VIDEO; + /* supported mbus codes are the same in isp video src pad */ + code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO; ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code, &dummy_cfg, code); -- cgit v1.2.3 From b861d139a36a4593498932bfec957bdcc7d98eb3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:16 +0200 Subject: media: staging: rkisp1: remove macro RKISP1_DIR_SINK_SRC The macro RKISP1_DIR_SINK_SRC is a mask of two flags. The macro hides the fact that it's a mask and the code is actually more clear if we replace it the with bitwise-or explicitly. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 6763379d6a65..8ea82ed1e644 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -25,7 +25,6 @@ #define RKISP1_DIR_SRC BIT(0) #define RKISP1_DIR_SINK BIT(1) -#define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) /* * NOTE: MIPI controller and input MUX are also configured in this file. @@ -69,84 +68,84 @@ static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, -- cgit v1.2.3 From c247818a873adcb8488021eed38c330ea8b288a3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:17 +0200 Subject: media: staging: rkisp1: rename macros 'RKISP1_DIR_*' to 'RKISP1_ISP_SD_*' The macros 'RKISP1_DIR_*' are flags that indicate on which pads of the isp subdevice the media bus code is supported. So the prefix RKISP1_ISP_SD_ is better. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 8ea82ed1e644..1d0b35133f65 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -23,8 +23,8 @@ #define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp" -#define RKISP1_DIR_SRC BIT(0) -#define RKISP1_DIR_SINK BIT(1) +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) /* * NOTE: MIPI controller and input MUX are also configured in this file. @@ -61,119 +61,119 @@ static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .pixel_enc = V4L2_PIXEL_ENC_YUV, - .direction = RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, }; @@ -571,9 +571,9 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, int pos = 0; if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) { - dir = RKISP1_DIR_SINK; + dir = RKISP1_ISP_SD_SINK; } else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) { - dir = RKISP1_DIR_SRC; + dir = RKISP1_ISP_SD_SRC; } else { if (code->index > 0) return -EINVAL; @@ -658,7 +658,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -742,7 +742,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } -- cgit v1.2.3 From 206003b18bb264521607440752814ccff59f91f3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:18 +0200 Subject: media: staging: rkisp1: rsz: set default format if the given format is not RKISP1_ISP_SD_SRC When setting the sink format of the 'rkisp1_resizer' the format should be supported by 'rkisp1_isp' on the video source pad. This patch checks this condition and sets the format to default if the condition is false. Fixes: 56e3b29f9f6b "media: staging: rkisp1: add streaming paths" Signed-off-by: Dafna Hirschfeld Reviewed-by: Tomasz Figa Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 3 +++ drivers/staging/media/rkisp1/rkisp1-isp.c | 3 --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 0c4fe503adc9..12bd9d05050d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -22,6 +22,9 @@ #include "rkisp1-regs.h" #include "uapi/rkisp1-config.h" +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) + #define RKISP1_ISP_MAX_WIDTH 4032 #define RKISP1_ISP_MAX_HEIGHT 3024 #define RKISP1_ISP_MIN_WIDTH 32 diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 1d0b35133f65..58c90c67594d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -23,9 +23,6 @@ #define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp" -#define RKISP1_ISP_SD_SRC BIT(0) -#define RKISP1_ISP_SD_SINK BIT(1) - /* * NOTE: MIPI controller and input MUX are also configured in this file. * This is because ISP Subdev describes not only ISP submodule (input size, diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 27f4b948ad41..8bc907ffa09b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -542,7 +542,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { sink_fmt->code = RKISP1_DEF_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } -- cgit v1.2.3 From 906dceb48dfa1e7c99c32e6b25878d47023e916b Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Wed, 17 Jun 2020 20:08:01 +0200 Subject: media: staging: rkisp1: rsz: fix resolution limitation on sink pad Resizer sink pad is limited by what the ISP can generate. The configurations describes what the resizer can produce. This was tested on a Scarlet device with ChromiumOs, where the selfpath receives 2592x1944 and produces 1600x1200 (which isn't possible without this fix). Fixes: 56e3b29f9f6b2 ("media: staging: rkisp1: add streaming paths") Signed-off-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 8bc907ffa09b..31aea7dfc976 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -553,11 +553,11 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, src_fmt->code = sink_fmt->code; sink_fmt->width = clamp_t(u32, format->width, - rsz->config->min_rsz_width, - rsz->config->max_rsz_width); + RKISP1_ISP_MIN_WIDTH, + RKISP1_ISP_MAX_WIDTH); sink_fmt->height = clamp_t(u32, format->height, - rsz->config->min_rsz_height, - rsz->config->max_rsz_height); + RKISP1_ISP_MIN_HEIGHT, + RKISP1_ISP_MAX_HEIGHT); *format = *sink_fmt; -- cgit v1.2.3 From 875c0d75954f022b2118f8dbfe57b96021de383b Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 29 May 2020 16:03:51 +0200 Subject: media: staging: rkisp1: set more precise size errors in debugfs When a size error is signaled, it is possible to read a register to see where the error comes from. So, in debugfs the general error 'pic_size_err' can be replaced with 3 more precise errors. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 4 +++- drivers/staging/media/rkisp1/rkisp1-dev.c | 9 +++++++-- drivers/staging/media/rkisp1/rkisp1-isp.c | 7 ++++++- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 12bd9d05050d..a9ede3f71ff3 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -229,7 +229,9 @@ struct rkisp1_resizer { struct rkisp1_debug { struct dentry *debugfs_dir; unsigned long data_loss; - unsigned long pic_size_error; + unsigned long outform_size_error; + unsigned long img_stabilization_size_error; + unsigned long inform_size_error; unsigned long mipi_error; unsigned long stats_error; unsigned long stop_timeout[2]; diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index f38801fea10d..a0eb8f08708b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -438,8 +438,13 @@ static void rkisp1_debug_init(struct rkisp1_device *rkisp1) } debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, &debug->data_loss); - debugfs_create_ulong("pic_size_error", 0444, debug->debugfs_dir, - &debug->pic_size_error); + debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, + &debug->outform_size_error); + debugfs_create_ulong("img_stabilization_size_error", 0444, + debug->debugfs_dir, + &debug->img_stabilization_size_error); + debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, + &debug->inform_size_error); debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, &debug->mipi_error); debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 58c90c67594d..76229ecbc41a 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -1122,8 +1122,13 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) { /* Clear pic_size_error */ isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR); + if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE) + rkisp1->debug.inform_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE) + rkisp1->debug.img_stabilization_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE) + rkisp1->debug.outform_size_error++; rkisp1_write(rkisp1, isp_err, RKISP1_CIF_ISP_ERR_CLR); - rkisp1->debug.pic_size_error++; } else if (status & RKISP1_CIF_ISP_DATA_LOSS) { /* keep track of data_loss in debugfs */ rkisp1->debug.data_loss++; -- cgit v1.2.3 From c19c7a13ee9d18bc6a43b417b8daa2edb825d601 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:45 +0200 Subject: media: staging: tegra-vde: Runtime PM is always available on Tegra Runtime PM is always available on Tegra nowadays since commit 40b2bb1b132a ("ARM: tegra: enforce PM requirement"), hence the case of unavailable RPM doesn't need to be handled. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 3fdf2cd0b99e..9dd9f0c4cae3 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1064,17 +1064,8 @@ static int tegra_vde_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_resume(dev); - if (err) - goto err_misc_unreg; - } - return 0; -err_misc_unreg: - misc_deregister(&vde->miscdev); - err_deinit_iommu: tegra_vde_iommu_deinit(vde); @@ -1089,13 +1080,6 @@ static int tegra_vde_remove(struct platform_device *pdev) { struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - int err; - - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_suspend(dev); - if (err) - return err; - } pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From 8f0c714ad9be1ef774c98e8819a7a571451cb019 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:46 +0200 Subject: media: staging: tegra-vde: Turn ON power domain on shutdown On some devices bootloader isn't ready to a clamped VDE power, and thus, machine hangs on a warm reboot (CPU reset). The VDE power partition is turned ON by default on a cold boot, hence VDE driver should keep power partition enabled on system's reboot too. This fixes hang on a warm reboot on a Tegra20 Acer A500 device, which is handy if Embedded Controller driver is unavailable, i.e. cold reboot can't be performed. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 9dd9f0c4cae3..3dcf5eab6b18 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1081,9 +1081,17 @@ static int tegra_vde_remove(struct platform_device *pdev) struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + pm_runtime_get_sync(dev); pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); + /* + * Balance RPM state, the VDE power domain is left ON and hardware + * is clock-gated. It's safe to reboot machine now. + */ + pm_runtime_put_noidle(dev); + clk_disable_unprepare(vde->clk); + misc_deregister(&vde->miscdev); tegra_vde_dmabuf_cache_unmap_all(vde); @@ -1095,6 +1103,16 @@ static int tegra_vde_remove(struct platform_device *pdev) return 0; } +static void tegra_vde_shutdown(struct platform_device *pdev) +{ + /* + * On some devices bootloader isn't ready to a power-gated VDE on + * a warm-reboot, machine will hang in that case. + */ + if (pm_runtime_status_suspended(&pdev->dev)) + tegra_vde_runtime_resume(&pdev->dev); +} + static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) { struct tegra_vde *vde = dev_get_drvdata(dev); @@ -1140,6 +1158,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match); static struct platform_driver tegra_vde_driver = { .probe = tegra_vde_probe, .remove = tegra_vde_remove, + .shutdown = tegra_vde_shutdown, .driver = { .name = "tegra-vde", .of_match_table = tegra_vde_of_match, -- cgit v1.2.3 From ebeacb1f612db22020561211c21b4d966cf7ca24 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:47 +0200 Subject: media: staging: tegra-vde: Power-cycle hardware on probe VDE partition is left turned ON after bootloader on most devices, hence let's ensure that it's turned OFF in order to lower power leakage while hardware is idling by turning it ON and OFF during of the driver's probe. Signed-off-by: Dmitry Osipenko Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 3dcf5eab6b18..a3c24d96d5b9 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1064,6 +1064,14 @@ static int tegra_vde_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); + /* + * VDE partition may be left ON after bootloader, hence let's + * power-cycle it in order to put hardware into a predictable lower + * power state. + */ + pm_runtime_get_sync(dev); + pm_runtime_put(dev); + return 0; err_deinit_iommu: -- cgit v1.2.3 From b3ab1c6058fad8cd5726f24e9ed9053e43bb2af4 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 24 Jun 2020 21:28:00 +0200 Subject: media: Add V4L2_TYPE_IS_CAPTURE helper It's all too easy to get confused by the V4L2_TYPE_IS_OUTPUT macro, when it's used as !V4L2_TYPE_IS_OUTPUT. Reduce the risk of confusion with macro to explicitly check for the CAPTURE queue type case. This change does not affect functionality, and it's only intended to make the code more readable. Suggested-by: Nicolas Dufresne Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: checkpatch: align with parenthesis] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 ++-- drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 2 +- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 7 +++---- drivers/media/platform/rcar_jpu.c | 2 +- drivers/media/platform/sti/hva/hva-v4l2.c | 2 +- drivers/media/platform/ti-vpe/vpe.c | 2 +- drivers/media/test-drivers/vicodec/vicodec-core.c | 6 +++--- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- drivers/staging/media/hantro/hantro_v4l2.c | 2 +- drivers/staging/media/rkvdec/rkvdec.c | 2 +- include/uapi/linux/videodev2.h | 2 ++ 13 files changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 57aa183bd198..30caad27281e 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -97,7 +97,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) unsigned int bytesused; unsigned int plane; - if (!V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_CAPTURE(b->type)) return 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { @@ -311,7 +311,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b /* Zero flags that we handle */ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; - if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { + if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) { /* * Non-COPY timestamps and non-OUTPUT queues will get * their timestamp and timestamp source flags from the diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index f6650b45bc3d..9f41c2e7097a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -577,7 +577,7 @@ int gsc_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, &tmp_h, min_h, max_h, mod_y, 0); - if (!V4L2_TYPE_IS_OUTPUT(s->type) && + if (V4L2_TYPE_IS_CAPTURE(s->type) && (ctx->gsc_ctrls.rotate->val == 90 || ctx->gsc_ctrls.rotate->val == 270)) gsc_check_crop_change(tmp_h, tmp_w, diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e2c162635f72..27a3c92c73bc 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -255,7 +255,7 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) if (IS_ERR(frame)) return PTR_ERR(frame); - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { for (i = 0; i < frame->fmt->num_planes; i++) vb2_set_plane_payload(vb, i, frame->payload[i]); } diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index f82a81a3bdee..61fed1e35a00 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -731,7 +731,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) * subsampling. Update capture queue when the stream is off. */ if (ctx->state == MTK_JPEG_SOURCE_CHANGE && - !V4L2_TYPE_IS_OUTPUT(q->type)) { + V4L2_TYPE_IS_CAPTURE(q->type)) { struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index bb9caaf513bc..724c7333b6e5 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -193,7 +193,7 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = fmt->pixelformat; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { pix_mp->colorspace = ctx->colorspace; pix_mp->xfer_func = ctx->xfer_func; pix_mp->ycbcr_enc = ctx->ycbcr_enc; @@ -327,9 +327,8 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w, &new_h, min_h, max_h, align_h); - if (!V4L2_TYPE_IS_OUTPUT(type) && - (ctx->ctrls.rotate->val == 90 || - ctx->ctrls.rotate->val == 270)) + if (V4L2_TYPE_IS_CAPTURE(type) && + (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) mtk_mdp_check_crop_change(new_h, new_w, &r->width, &r->height); else diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 5250a14324e9..9b99ff368698 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1066,7 +1066,7 @@ static int jpu_buf_prepare(struct vb2_buffer *vb) } /* decoder capture queue */ - if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) vb2_set_plane_payload(vb, i, size); } diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 197b99d8fd9c..bb34d6997d99 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1087,7 +1087,7 @@ static void hva_stop_streaming(struct vb2_queue *vq) if ((V4L2_TYPE_IS_OUTPUT(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || - (!V4L2_TYPE_IS_OUTPUT(vq->type) && + (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { dev_dbg(dev, "%s %s out=%d cap=%d\n", ctx->name, to_type_str(vq->type), diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index cff2fcd6d812..346f8212791c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1576,7 +1576,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) *f = q_data->format; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { struct vpe_q_data *s_q_data; struct v4l2_pix_format_mplane *spix; diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index e879290727ef..8941d73f6611 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1442,7 +1442,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { unsigned int i; @@ -1479,7 +1479,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) * in the compressed stream */ if (ctx->is_stateless || ctx->is_enc || - !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } @@ -1574,7 +1574,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || - (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) return 0; if (info->id == V4L2_PIX_FMT_FWHT || diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 62ac9424c92a..95a8f2dc5341 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -556,7 +556,7 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ret = vb2_querybuf(vq, buf); /* Adjust MMAP memory offsets for the CAPTURE queue */ - if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset @@ -712,7 +712,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { dprintk("%s: requests cannot be used with capture buffers\n", __func__); @@ -729,7 +729,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * buffer as DONE with LAST flag since it won't be queued on the * device. */ - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) v4l2_m2m_force_last_buf_done(m2m_ctx, vq); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index f28a94e2fa93..63859e8a0923 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -237,7 +237,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, enum v4l2_buf_type type) { const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = !V4L2_TYPE_IS_OUTPUT(type); + bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; coded = capture == hantro_is_encoder_ctx(ctx); diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 225eeca73356..fd68671f0286 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -489,7 +489,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) const struct rkvdec_coded_fmt_desc *desc; int ret; - if (!V4L2_TYPE_IS_OUTPUT(q->type)) + if (V4L2_TYPE_IS_CAPTURE(q->type)) return 0; desc = ctx->coded_fmt_desc; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 303805438814..c7b70ff53bc1 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -171,6 +171,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ || (type) == V4L2_BUF_TYPE_META_OUTPUT) +#define V4L2_TYPE_IS_CAPTURE(type) (!V4L2_TYPE_IS_OUTPUT(type)) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, -- cgit v1.2.3 From 24385f9c6ee4e1e118046333b49637e5d337d943 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:38 +0200 Subject: media: staging: rkisp1: use a macro for the statistics flags mask The mask of the ready statistics flags is used in several places in the code using bitwise-or. Use a macro for that to make the code more readable. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 4 ++++ drivers/staging/media/rkisp1/rkisp1-isp.c | 5 +---- drivers/staging/media/rkisp1/rkisp1-stats.c | 14 +++----------- 3 files changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index a9ede3f71ff3..43fe4dc2c7c4 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -45,6 +45,10 @@ #define RKISP1_MAX_BUS_CLK 8 +#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \ + RKISP1_CIF_ISP_AFM_FIN | \ + RKISP1_CIF_ISP_EXP_END | \ + RKISP1_CIF_ISP_HIST_MEASURE_RDY) enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, RKISP1_RSZ_PAD_SRC, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 76229ecbc41a..6ec1e9816e9f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -1139,10 +1139,7 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) /* New frame from the sensor received */ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS); - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_ris & RKISP1_STATS_MEAS_MASK) rkisp1_stats_isr(&rkisp1->stats, isp_ris); } diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 0616793ae395..b19a6d9cdd4d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -396,26 +396,18 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) struct rkisp1_device *rkisp1 = stats->rkisp1; struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; - u32 val; spin_lock(&stats->irq_lock); - val = RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY; - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_ICR); + rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS); - if (isp_mis_tmp & - (RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK) rkisp1->debug.stats_error++; if (!stats->is_streaming) goto unlock; - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) { + if (isp_ris & RKISP1_STATS_MEAS_MASK) { work = kzalloc(sizeof(*work), GFP_ATOMIC); if (work) { INIT_WORK(&work->work, -- cgit v1.2.3 From 46c308a964fe6515845eacb8765a2c2e2b8c1570 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:39 +0200 Subject: media: staging: rkisp1: stats: replace spin_lock_irqsave with spin_lock_irq The function 'rkisp1_stats_vb2_stop_streaming' runs in user context therefore it is enough to use spin_lock_irq Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index b19a6d9cdd4d..d4f0df4342e0 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -152,13 +152,12 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_stats *stats = vq->drv_priv; struct rkisp1_buffer *buf; - unsigned long flags; unsigned int i; /* Make sure no new work queued in isr before draining wq */ - spin_lock_irqsave(&stats->irq_lock, flags); + spin_lock_irq(&stats->irq_lock); stats->is_streaming = false; - spin_unlock_irqrestore(&stats->irq_lock, flags); + spin_unlock_irq(&stats->irq_lock); drain_workqueue(stats->readout_wq); -- cgit v1.2.3 From b512ab7a85268b3d43d8c504a3e84dbdd923c52b Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:40 +0200 Subject: media: staging: rkisp1: stats: replace locks wq_lock, irq_lock with one lock This patch removes two locks in the rkisp1_stats struct: - The mutex 'wq_lock' that is used to protect the buffers list 'stat' - The spin_lock 'irq_lock' that is used to protect the variable 'is_streaming' It replaces them with one spin_lock 'lock' that protects both the buffers list and the 'is_streaming' variable. In later patch the reading of the statistics will move to the isr so there will be no need for the mutex 'wq_lock' Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 5 ++--- drivers/staging/media/rkisp1/rkisp1-stats.c | 25 +++++++++++-------------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 43fe4dc2c7c4..d62f5d6fbc81 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -181,7 +181,7 @@ struct rkisp1_capture { /* * struct rkisp1_stats - ISP Statistics device * - * @irq_lock: buffer queue lock + * @lock: locks the buffer list 'stat' and 'is_streaming' * @stat: stats buffer list * @readout_wq: workqueue for statistics information read */ @@ -189,13 +189,12 @@ struct rkisp1_stats { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; - spinlock_t irq_lock; + spinlock_t lock; /* locks 'is_streaming', and 'stats' */ struct list_head stat; struct v4l2_format vdev_fmt; bool is_streaming; struct workqueue_struct *readout_wq; - struct mutex wq_lock; }; /* diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index d4f0df4342e0..58329e6b0598 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -133,9 +133,9 @@ static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb) stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0); - mutex_lock(&stats_dev->wq_lock); + spin_lock_irq(&stats_dev->lock); list_add_tail(&stats_buf->queue, &stats_dev->stat); - mutex_unlock(&stats_dev->wq_lock); + spin_unlock_irq(&stats_dev->lock); } static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb) @@ -155,13 +155,13 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) unsigned int i; /* Make sure no new work queued in isr before draining wq */ - spin_lock_irq(&stats->irq_lock); + spin_lock_irq(&stats->lock); stats->is_streaming = false; - spin_unlock_irq(&stats->irq_lock); + spin_unlock_irq(&stats->lock); drain_workqueue(stats->readout_wq); - mutex_lock(&stats->wq_lock); + spin_lock_irq(&stats->lock); for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -170,7 +170,7 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) list_del(&buf->queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - mutex_unlock(&stats->wq_lock); + spin_unlock_irq(&stats->lock); } static int @@ -340,14 +340,14 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, frame_sequence = meas_work->frame_id; } - mutex_lock(&stats->wq_lock); + spin_lock_irq(&stats->lock); /* get one empty buffer */ if (!list_empty(&stats->stat)) { cur_buf = list_first_entry(&stats->stat, struct rkisp1_buffer, queue); list_del(&cur_buf->queue); } - mutex_unlock(&stats->wq_lock); + spin_unlock_irq(&stats->lock); if (!cur_buf) return; @@ -396,7 +396,7 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; - spin_lock(&stats->irq_lock); + spin_lock(&stats->lock); rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); @@ -425,7 +425,7 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) } unlock: - spin_unlock(&stats->irq_lock); + spin_unlock(&stats->lock); } static void rkisp1_init_stats(struct rkisp1_stats *stats) @@ -445,10 +445,9 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, int ret; stats->rkisp1 = rkisp1; - mutex_init(&stats->wq_lock); mutex_init(&node->vlock); INIT_LIST_HEAD(&stats->stat); - spin_lock_init(&stats->irq_lock); + spin_lock_init(&stats->lock); strscpy(vdev->name, RKISP1_STATS_DEV_NAME, sizeof(vdev->name)); @@ -495,7 +494,6 @@ err_cleanup_media_entity: err_release_queue: vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); return ret; } @@ -509,5 +507,4 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats) media_entity_cleanup(&vdev->entity); vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); } -- cgit v1.2.3 From 1fcc0244b5e6c4f557eecb656594ce29214da209 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:41 +0200 Subject: media: staging: rkisp1: stats: read the stats in the isr Currently the stats are read in a work queue. Defering the reading of the stats is not needed and it is fine to read them inside the irq handler. This patch fixes and remove the TODO item: 'Use threaded interrupt for rkisp1_stats_isr(), remove work queue.' Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/TODO | 1 - drivers/staging/media/rkisp1/rkisp1-common.h | 3 - drivers/staging/media/rkisp1/rkisp1-stats.c | 87 +++------------------------- 3 files changed, 7 insertions(+), 84 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO index c0cbec0a164d..bdb1b8f73556 100644 --- a/drivers/staging/media/rkisp1/TODO +++ b/drivers/staging/media/rkisp1/TODO @@ -1,5 +1,4 @@ * Fix pad format size for statistics and parameters entities. -* Use threaded interrupt for rkisp1_stats_isr(), remove work queue. * Fix checkpatch errors. * Review and comment every lock * Handle quantization diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index d62f5d6fbc81..2603a5aa5210 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -183,7 +183,6 @@ struct rkisp1_capture { * * @lock: locks the buffer list 'stat' and 'is_streaming' * @stat: stats buffer list - * @readout_wq: workqueue for statistics information read */ struct rkisp1_stats { struct rkisp1_vdev_node vnode; @@ -193,8 +192,6 @@ struct rkisp1_stats { struct list_head stat; struct v4l2_format vdev_fmt; bool is_streaming; - - struct workqueue_struct *readout_wq; }; /* diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 58329e6b0598..87e4104d20dd 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -18,21 +18,6 @@ #define RKISP1_ISP_STATS_REQ_BUFS_MIN 2 #define RKISP1_ISP_STATS_REQ_BUFS_MAX 8 -enum rkisp1_isp_readout_cmd { - RKISP1_ISP_READOUT_MEAS, - RKISP1_ISP_READOUT_META, -}; - -struct rkisp1_isp_readout_work { - struct work_struct work; - struct rkisp1_stats *stats; - - unsigned int frame_id; - unsigned int isp_ris; - enum rkisp1_isp_readout_cmd readout; - struct vb2_buffer *vb; -}; - static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -154,14 +139,8 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) struct rkisp1_buffer *buf; unsigned int i; - /* Make sure no new work queued in isr before draining wq */ spin_lock_irq(&stats->lock); stats->is_streaming = false; - spin_unlock_irq(&stats->lock); - - drain_workqueue(stats->readout_wq); - - spin_lock_irq(&stats->lock); for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -324,8 +303,7 @@ static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, } static void -rkisp1_stats_send_measurement(struct rkisp1_stats *stats, - struct rkisp1_isp_readout_work *meas_work) +rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris) { struct rkisp1_stat_buffer *cur_stat_buf; struct rkisp1_buffer *cur_buf = NULL; @@ -333,21 +311,12 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, atomic_read(&stats->rkisp1->isp.frame_sequence); u64 timestamp = ktime_get_ns(); - if (frame_sequence != meas_work->frame_id) { - dev_warn(stats->rkisp1->dev, - "Measurement late(%d, %d)\n", - frame_sequence, meas_work->frame_id); - frame_sequence = meas_work->frame_id; - } - - spin_lock_irq(&stats->lock); /* get one empty buffer */ if (!list_empty(&stats->stat)) { cur_buf = list_first_entry(&stats->stat, struct rkisp1_buffer, queue); list_del(&cur_buf->queue); } - spin_unlock_irq(&stats->lock); if (!cur_buf) return; @@ -355,18 +324,18 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, cur_stat_buf = (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) + if (isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) + if (isp_ris & RKISP1_CIF_ISP_AFM_FIN) rkisp1_stats_get_afc_meas(stats, cur_stat_buf); - if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) { + if (isp_ris & RKISP1_CIF_ISP_EXP_END) { rkisp1_stats_get_aec_meas(stats, cur_stat_buf); rkisp1_stats_get_bls_meas(stats, cur_stat_buf); } - if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) + if (isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) rkisp1_stats_get_hst_meas(stats, cur_stat_buf); vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, @@ -376,24 +345,9 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } -static void rkisp1_stats_readout_work(struct work_struct *work) -{ - struct rkisp1_isp_readout_work *readout_work = - container_of(work, struct rkisp1_isp_readout_work, work); - struct rkisp1_stats *stats = readout_work->stats; - - if (readout_work->readout == RKISP1_ISP_READOUT_MEAS) - rkisp1_stats_send_measurement(stats, readout_work); - - kfree(readout_work); -} - void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) { - unsigned int frame_sequence = - atomic_read(&stats->rkisp1->isp.frame_sequence); struct rkisp1_device *rkisp1 = stats->rkisp1; - struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; spin_lock(&stats->lock); @@ -406,23 +360,8 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) if (!stats->is_streaming) goto unlock; - if (isp_ris & RKISP1_STATS_MEAS_MASK) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (work) { - INIT_WORK(&work->work, - rkisp1_stats_readout_work); - work->readout = RKISP1_ISP_READOUT_MEAS; - work->stats = stats; - work->frame_id = frame_sequence; - work->isp_ris = isp_ris; - if (!queue_work(stats->readout_wq, - &work->work)) - kfree(work); - } else { - dev_err(stats->rkisp1->dev, - "Could not allocate work\n"); - } - } + if (isp_ris & RKISP1_STATS_MEAS_MASK) + rkisp1_stats_send_measurement(stats, isp_ris); unlock: spin_unlock(&stats->lock); @@ -476,19 +415,8 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, goto err_cleanup_media_entity; } - stats->readout_wq = alloc_workqueue("measurement_queue", - WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - - if (!stats->readout_wq) { - ret = -ENOMEM; - goto err_unreg_vdev; - } - return 0; -err_unreg_vdev: - video_unregister_device(vdev); err_cleanup_media_entity: media_entity_cleanup(&vdev->entity); err_release_queue: @@ -502,7 +430,6 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats) struct rkisp1_vdev_node *node = &stats->vnode; struct video_device *vdev = &node->vdev; - destroy_workqueue(stats->readout_wq); video_unregister_device(vdev); media_entity_cleanup(&vdev->entity); vb2_queue_release(vdev->queue); -- cgit v1.2.3 From e4fa530fd4b1f31b46d51b6b245359e0116cb662 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 7 Jul 2020 20:10:53 +0200 Subject: media: staging: rkisp1: remove duplicate macro definition The macro RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN is defined twice. Remove the second define. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-regs.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-regs.h b/drivers/staging/media/rkisp1/rkisp1-regs.h index 46018f435b6f..9b8e616ea24c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-regs.h +++ b/drivers/staging/media/rkisp1/rkisp1-regs.h @@ -398,7 +398,6 @@ #define RKISP1_CIF_ISP_AWB_YMAX_READ(x) (((x) >> 2) & 1) #define RKISP1_CIF_ISP_AWB_MODE_RGB_EN ((1 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) -#define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_MASK_NONE 0xFFFFFFFC #define RKISP1_CIF_ISP_AWB_MODE_READ(x) ((x) & 3) /* ISP_AWB_GAIN_RB, ISP_AWB_GAIN_G */ -- cgit v1.2.3 From 0b62c7db458ba97f93442a5ed39a2d636362d19f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 7 Jul 2020 20:16:30 +0200 Subject: media: staging: rkisp1: fix typos in file rkisp1-config.h Fix two spelling typos in file rkisp1-config.h. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/uapi/rkisp1-config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h index ca0d031b14ac..8f9b061e5b6b 100644 --- a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h +++ b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h @@ -68,7 +68,7 @@ #define RKISP1_CIF_ISP_BLS_FIX_MASK 0x00001fff /* - * Automatic white balance measurments + * Automatic white balance measurements */ #define RKISP1_CIF_ISP_AWB_MAX_GRID 1 #define RKISP1_CIF_ISP_AWB_MAX_FRAMES 7 @@ -408,7 +408,7 @@ struct rkisp1_cif_isp_flt_config { /** * struct rkisp1_cif_isp_bdm_config - Configuration used by Bayer DeMosaic * - * @demosaic_th: threshod for bayer demosaicing texture detection + * @demosaic_th: threshold for bayer demosaicing texture detection */ struct rkisp1_cif_isp_bdm_config { __u8 demosaic_th; -- cgit v1.2.3 From 3ebf271b1dee6df816bd8f2135218640c478dedd Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:02 +0200 Subject: hantro: h264: Remove unused macro definition The generic H264 reference list builder moved all the users of this macro, but left the macro. Remove it. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h264.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index d561f125085a..dd935d7009bf 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -22,8 +22,6 @@ #define POC_BUFFER_SIZE 34 #define SCALING_LIST_SIZE (6 * 16 + 2 * 64) -#define HANTRO_CMP(a, b) ((a) < (b) ? -1 : 1) - /* Data structure describing auxiliary buffer format. */ struct hantro_h264_dec_priv_tbl { u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; -- cgit v1.2.3 From 4df3a47e3422a9de1f3ce1a4ba8a0447a73e7567 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:03 +0200 Subject: hantro: h264: Rename scaling list handling function Commit e17f08e31666 ("media: hantro: Do not reorder H264 scaling list") removed the scaling list reordering, which was wrong and not needed. However, the name of the function stayed, which is confusing for anyone reading the code. Rename from "reorder" to "assemble" which is cleaner. This is just a cosmetic cleanup. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h264.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index dd935d7009bf..194d05848077 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -193,7 +193,7 @@ static const u32 h264_cabac_table[] = { }; static void -reorder_scaling_list(struct hantro_ctx *ctx) +assemble_scaling_list(struct hantro_ctx *ctx) { const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; @@ -235,7 +235,7 @@ static void prepare_table(struct hantro_ctx *ctx) tbl->poc[32] = dec_param->top_field_order_cnt; tbl->poc[33] = dec_param->bottom_field_order_cnt; - reorder_scaling_list(ctx); + assemble_scaling_list(ctx); } static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, -- cgit v1.2.3 From 21f0315b7b3ee6ca909d81a963744671fb27bf71 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:04 +0200 Subject: hantro: Rework how encoder and decoder are identified So far we've been using the .buf_finish hook to distinguish decoder from encoder. This is unnecessarily obfuscated. Moreover, we want to move the buf_finish, so use a cleaner scheme to distinguish the driver decoder/encoder type. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro.h | 6 +++--- drivers/staging/media/hantro/hantro_drv.c | 9 +++------ drivers/staging/media/hantro/hantro_v4l2.c | 28 ++++++++++++++-------------- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 3005207fc6fb..2284e23d8500 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -199,6 +199,7 @@ struct hantro_dev { * * @dev: VPU driver data to which the context belongs. * @fh: V4L2 file handler. + * @is_encoder: Decoder or encoder context? * * @sequence_cap: Sequence counter for capture queue * @sequence_out: Sequence counter for output queue @@ -223,6 +224,7 @@ struct hantro_dev { struct hantro_ctx { struct hantro_dev *dev; struct v4l2_fh fh; + bool is_encoder; u32 sequence_cap; u32 sequence_out; @@ -399,8 +401,6 @@ static inline void hantro_reg_write_s(struct hantro_dev *vpu, vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); } -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx); - void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); @@ -420,7 +420,7 @@ static inline bool hantro_needs_postproc(const struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; + return !ctx->is_encoder && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 0db8ad455160..9145d02e5d3c 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -195,11 +195,6 @@ err_cancel_job: hantro_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); } -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx) -{ - return ctx->buf_finish == hantro_enc_buf_finish; -} - static struct v4l2_m2m_ops vpu_m2m_ops = { .device_run = device_run, }; @@ -240,7 +235,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) * * For the DMA destination buffer, we use a bounce buffer. */ - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { dst_vq->mem_ops = &vb2_vmalloc_memops; } else { dst_vq->bidirectional = true; @@ -420,8 +415,10 @@ static int hantro_open(struct file *filp) if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; ctx->buf_finish = hantro_enc_buf_finish; + ctx->is_encoder = true; } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; + ctx->is_encoder = false; } else { ret = -ENODEV; goto err_ctx_free; diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index 63859e8a0923..b668a82d40ad 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -40,7 +40,7 @@ hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { const struct hantro_fmt *formats; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { formats = ctx->dev->variant->enc_fmts; *num_fmts = ctx->dev->variant->num_enc_fmts; } else { @@ -55,7 +55,7 @@ static const struct hantro_fmt * hantro_get_postproc_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { *num_fmts = 0; return NULL; } @@ -158,7 +158,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, * not MODE_NONE. * - on the output side we want to filter out all MODE_NONE formats. */ - skip_mode_none = capture == hantro_is_encoder_ctx(ctx); + skip_mode_none = capture == ctx->is_encoder; formats = hantro_get_formats(ctx, &num_fmts); for (i = 0; i < num_fmts; i++) { @@ -240,7 +240,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; - coded = capture == hantro_is_encoder_ctx(ctx); + coded = capture == ctx->is_encoder; vpu_debug(4, "trying format %c%c%c%c\n", (pix_mp->pixelformat & 0x7f), @@ -257,7 +257,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, if (coded) { pix_mp->num_planes = 1; vpu_fmt = fmt; - } else if (hantro_is_encoder_ctx(ctx)) { + } else if (ctx->is_encoder) { vpu_fmt = ctx->vpu_dst_fmt; } else { vpu_fmt = ctx->vpu_src_fmt; @@ -330,7 +330,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) vpu_fmt = hantro_get_default_fmt(ctx, true); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_dst_fmt = vpu_fmt; fmt = &ctx->dst_fmt; } else { @@ -341,7 +341,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(fmt, vpu_fmt); fmt->width = vpu_fmt->frmsize.min_width; fmt->height = vpu_fmt->frmsize.min_height; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_cap(ctx, fmt); else hantro_set_fmt_out(ctx, fmt); @@ -355,7 +355,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) raw_vpu_fmt = hantro_get_default_fmt(ctx, false); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_src_fmt = raw_vpu_fmt; raw_fmt = &ctx->src_fmt; encoded_fmt = &ctx->dst_fmt; @@ -368,7 +368,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(raw_fmt, raw_vpu_fmt); raw_fmt->width = encoded_fmt->width; raw_fmt->width = encoded_fmt->width; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_out(ctx, raw_fmt); else hantro_set_fmt_cap(ctx, raw_fmt); @@ -409,7 +409,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, if (ret) return ret; - if (!hantro_is_encoder_ctx(ctx)) { + if (!ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -450,7 +450,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (!hantro_is_encoder_ctx(ctx)) + if (!ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -479,7 +479,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, if (vb2_is_busy(vq)) return -EBUSY; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -512,7 +512,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -655,7 +655,7 @@ static bool hantro_vq_is_coded(struct vb2_queue *q) { struct hantro_ctx *ctx = vb2_get_drv_priv(q); - return hantro_is_encoder_ctx(ctx) != V4L2_TYPE_IS_OUTPUT(q->type); + return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); } static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) -- cgit v1.2.3 From e765dba11ec26d7ea42974ec4d470b5ce00be3de Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:05 +0200 Subject: hantro: Move hantro_enc_buf_finish to JPEG codec_ops.done hantro_enc_buf_finish is used only for JPEG, and so should be moved to JPEG codec_ops.done. This cleanup is also taking care of addressing a subtle issue: checking the non-NULL bounce buffer using ctx->jpeg_enc, which is a member of a union is confusing and error-prone. Note that the issue is currently innocuous because an encoder context only supports JPEG. The codec_ops.done has an argument that codec-specific code shouldn't need, so drop that as well. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro.h | 7 ----- drivers/staging/media/hantro/hantro_drv.c | 37 +++-------------------- drivers/staging/media/hantro/hantro_h1_jpeg_enc.c | 17 +++++++++++ drivers/staging/media/hantro/hantro_hw.h | 3 +- drivers/staging/media/hantro/rk3288_vpu_hw.c | 1 + 5 files changed, 24 insertions(+), 41 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 2284e23d8500..65f9f7ea7dcf 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -212,9 +212,6 @@ struct hantro_dev { * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. * - * @buf_finish: Buffer finish. This depends on encoder or decoder - * context, and it's called right before - * calling v4l2_m2m_job_finish. * @codec_ops: Set of operations related to codec mode. * @postproc: Post-processing context. * @jpeg_enc: JPEG-encoding context. @@ -237,10 +234,6 @@ struct hantro_ctx { struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; - int (*buf_finish)(struct hantro_ctx *ctx, - struct vb2_buffer *buf, - unsigned int bytesused); - const struct hantro_codec_ops *codec_ops; struct hantro_postproc_ctx postproc; diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 9145d02e5d3c..88b5c5989d83 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -56,37 +56,12 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) return hantro_get_dec_buf_addr(ctx, buf); } -static int -hantro_enc_buf_finish(struct hantro_ctx *ctx, struct vb2_buffer *buf, - unsigned int bytesused) -{ - size_t avail_size; - - avail_size = vb2_plane_size(buf, 0) - ctx->vpu_dst_fmt->header_size; - if (bytesused > avail_size) - return -EINVAL; - /* - * The bounce buffer is only for the JPEG encoder. - * TODO: Rework the JPEG encoder to eliminate the need - * for a bounce buffer. - */ - if (ctx->jpeg_enc.bounce_buffer.cpu) { - memcpy(vb2_plane_vaddr(buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->jpeg_enc.bounce_buffer.cpu, bytesused); - } - buf->planes[0].bytesused = - ctx->vpu_dst_fmt->header_size + bytesused; - return 0; -} - static void hantro_job_finish(struct hantro_dev *vpu, struct hantro_ctx *ctx, unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; - int ret; pm_runtime_mark_last_busy(vpu->dev); pm_runtime_put_autosuspend(vpu->dev); @@ -103,12 +78,6 @@ static void hantro_job_finish(struct hantro_dev *vpu, src->sequence = ctx->sequence_out++; dst->sequence = ctx->sequence_cap++; - if (ctx->buf_finish) { - ret = ctx->buf_finish(ctx, &dst->vb2_buf, bytesused); - if (ret) - result = VB2_BUF_STATE_ERROR; - } - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, result); } @@ -124,8 +93,11 @@ void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, * the timeout expired. The watchdog is running, * and will take care of finishing the job. */ - if (cancel_delayed_work(&vpu->watchdog_work)) + if (cancel_delayed_work(&vpu->watchdog_work)) { + if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) + ctx->codec_ops->done(ctx); hantro_job_finish(vpu, ctx, bytesused, result); + } } void hantro_watchdog(struct work_struct *work) @@ -414,7 +386,6 @@ static int hantro_open(struct file *filp) ctx->dev = vpu; if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; - ctx->buf_finish = hantro_enc_buf_finish; ctx->is_encoder = true; } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index b22418436823..b88dc4ed06db 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -137,3 +137,20 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) vepu_write(vpu, reg, H1_REG_ENC_CTRL); } + +void hantro_jpeg_enc_done(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; + struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); + + /* + * TODO: Rework the JPEG encoder to eliminate the need + * for a bounce buffer. + */ + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_dst_fmt->header_size, + ctx->jpeg_enc.bounce_buffer.cpu, bytesused); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + ctx->vpu_dst_fmt->header_size + bytesused); +} diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 4053d8710e04..2d6323cd6732 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -138,7 +138,7 @@ struct hantro_codec_ops { int (*init)(struct hantro_ctx *ctx); void (*exit)(struct hantro_ctx *ctx); void (*run)(struct hantro_ctx *ctx); - void (*done)(struct hantro_ctx *ctx, enum vb2_buffer_state); + void (*done)(struct hantro_ctx *ctx); void (*reset)(struct hantro_ctx *ctx); }; @@ -172,6 +172,7 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx); int hantro_jpeg_enc_init(struct hantro_ctx *ctx); void hantro_jpeg_enc_exit(struct hantro_ctx *ctx); +void hantro_jpeg_enc_done(struct hantro_ctx *ctx); dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, unsigned int dpb_idx); diff --git a/drivers/staging/media/hantro/rk3288_vpu_hw.c b/drivers/staging/media/hantro/rk3288_vpu_hw.c index 2f914b37b9e5..b1cf2abb972f 100644 --- a/drivers/staging/media/hantro/rk3288_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3288_vpu_hw.c @@ -180,6 +180,7 @@ static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { .run = hantro_h1_jpeg_enc_run, .reset = rk3288_vpu_enc_reset, .init = hantro_jpeg_enc_init, + .done = hantro_jpeg_enc_done, .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { -- cgit v1.2.3 From b72a6342dd240ce8e15b7acf1c38c67a0c56092b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:06 +0200 Subject: hantro: Remove unused bytesused argument The driver doesn't need the bytesused argument. For decoders, the plane bytesused is known and therefore, buf_prepare is used to set it. For encoders, it's handled by the codec_ops.done hook. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 9 ++++----- drivers/staging/media/hantro/hantro_hw.h | 2 +- drivers/staging/media/hantro/imx8m_vpu_hw.c | 2 +- drivers/staging/media/hantro/rk3288_vpu_hw.c | 7 +++---- drivers/staging/media/hantro/rk3399_vpu_hw.c | 7 +++---- 5 files changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 88b5c5989d83..34367b169011 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -58,7 +58,6 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) static void hantro_job_finish(struct hantro_dev *vpu, struct hantro_ctx *ctx, - unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; @@ -82,7 +81,7 @@ static void hantro_job_finish(struct hantro_dev *vpu, result); } -void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result) { struct hantro_ctx *ctx = @@ -96,7 +95,7 @@ void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, if (cancel_delayed_work(&vpu->watchdog_work)) { if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) ctx->codec_ops->done(ctx); - hantro_job_finish(vpu, ctx, bytesused, result); + hantro_job_finish(vpu, ctx, result); } } @@ -111,7 +110,7 @@ void hantro_watchdog(struct work_struct *work) if (ctx) { vpu_err("frame processing timed out!\n"); ctx->codec_ops->reset(ctx); - hantro_job_finish(vpu, ctx, 0, VB2_BUF_STATE_ERROR); + hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); } } @@ -164,7 +163,7 @@ static void device_run(void *priv) return; err_cancel_job: - hantro_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); + hantro_job_finish(ctx->dev, ctx, VB2_BUF_STATE_ERROR); } static struct v4l2_m2m_ops vpu_m2m_ops = { diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 2d6323cd6732..f066de6b592d 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -163,7 +163,7 @@ extern const u32 hantro_vp8_dec_mc_filter[8][6]; void hantro_watchdog(struct work_struct *work); void hantro_run(struct hantro_ctx *ctx); -void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); void hantro_end_prepare_run(struct hantro_ctx *ctx); diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c index cb2420c5526e..c222de075ef4 100644 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -143,7 +143,7 @@ static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/hantro/rk3288_vpu_hw.c b/drivers/staging/media/hantro/rk3288_vpu_hw.c index b1cf2abb972f..7b299ee3e93d 100644 --- a/drivers/staging/media/hantro/rk3288_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3288_vpu_hw.c @@ -113,17 +113,16 @@ static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, H1_REG_INTERRUPT); - bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, H1_REG_INTERRUPT); vepu_write(vpu, 0, H1_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -141,7 +140,7 @@ static irqreturn_t rk3288_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw.c b/drivers/staging/media/hantro/rk3399_vpu_hw.c index 9ac1f2cb6a16..7a7962cf771e 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw.c @@ -92,17 +92,16 @@ static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, VEPU_REG_INTERRUPT); - bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, VEPU_REG_INTERRUPT); vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -120,7 +119,7 @@ static irqreturn_t rk3399_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } -- cgit v1.2.3 From 46d7aaebbe441d5381e35d8e16df784690e65ef3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:07 +0200 Subject: hantro: Make sure we don't use post-processor on an encoder Commit 986eee3a5234 ("media: hantro: Prevent encoders from using post-processing") fixed hantro_needs_postproc condition, but missed one case. Encoders don't have any post-processor hardware block, so also can't be disabled. Fix it. Fixes: 986eee3a5234 ("media: hantro: Prevent encoders from using post-processing") Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 34367b169011..d32b6b1ab70b 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -122,10 +122,12 @@ void hantro_start_prepare_run(struct hantro_ctx *ctx) v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, &ctx->ctrl_handler); - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); + if (!ctx->is_encoder) { + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + hantro_postproc_enable(ctx); + else + hantro_postproc_disable(ctx); + } } void hantro_end_prepare_run(struct hantro_ctx *ctx) -- cgit v1.2.3 From 9363aa33f6a9acfd16f98c749f17f6c65d184670 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 9 Jul 2020 18:36:34 +0200 Subject: rkvdec: h264: Refuse to decode unsupported bitstream The hardware only supports 4:2:2, 4:2:0 or 4:0:0 (monochrome), 8-bit or 10-bit depth content. Verify that the SPS refers to a supported bitstream, and refuse unsupported bitstreams by failing at TRY_EXT_CTRLS time. The driver is currently broken on 10-bit and 4:2:2 so disallow those as well. Signed-off-by: Ezequiel Garcia Reviewed-by: Jonas Karlman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkvdec/rkvdec.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index fd68671f0286..c8151328fb70 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,6 +27,32 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + /* + * TODO: The hardware supports 10-bit and 4:2:2 profiles, + * but it's currently broken in the driver. + * Reject them for now, until it's fixed. + */ + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { + .try_ctrl = rkvdec_try_ctrl, +}; + static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .per_request = true, @@ -42,6 +68,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { .per_request = true, .mandatory = true, .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .cfg.ops = &rkvdec_ctrl_ops, }, { .per_request = true, -- cgit v1.2.3 From d70cca7323442026e20c474314518c446cb4766f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 9 Jul 2020 18:36:35 +0200 Subject: hantro: h264: Refuse to decode unsupported bitstream The hardware only supports 4:2:0 or 4:0:0 (monochrome), 8-bit depth content. Verify that the SPS refers to a supported bitstream, and refuse unsupported bitstreams by failing at TRY_EXT_CTRLS time. Given the JPEG compression level control is the only one that needs setting, a specific ops is provided. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Reviewed-by: Jonas Karlman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index d32b6b1ab70b..34797507f214 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -229,7 +229,25 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) return vb2_queue_init(dst_vq); } -static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) +static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { struct hantro_ctx *ctx; @@ -250,7 +268,11 @@ static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) } static const struct v4l2_ctrl_ops hantro_ctrl_ops = { - .s_ctrl = hantro_s_ctrl, + .try_ctrl = hantro_try_ctrl, +}; + +static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { + .s_ctrl = hantro_jpeg_s_ctrl, }; static const struct hantro_ctrl controls[] = { @@ -262,7 +284,7 @@ static const struct hantro_ctrl controls[] = { .max = 100, .step = 1, .def = 50, - .ops = &hantro_ctrl_ops, + .ops = &hantro_jpeg_ctrl_ops, }, }, { .codec = HANTRO_MPEG2_DECODER, @@ -293,6 +315,7 @@ static const struct hantro_ctrl controls[] = { .codec = HANTRO_H264_DECODER, .cfg = { .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .ops = &hantro_ctrl_ops, }, }, { .codec = HANTRO_H264_DECODER, -- cgit v1.2.3 From f0e226885c3c18b2e7e27b8043c5a393405b037e Mon Sep 17 00:00:00 2001 From: Krzysztof HaÅ‚asa Date: Thu, 14 May 2020 12:00:47 +0200 Subject: staging/media: Remove unneeded geometry restrictions from i.MX CSI driver I don't know what minimal image dimensions are, but 32x32 appears to be ok according to the docs. This is needed for small sensors like 80x80 thermal imagers. Signed-off-by: Krzysztof Halasa Reviewed-by: Philipp Zabel Tested-by: Philipp Zabel Acked-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prp.c | 4 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 4 ++-- drivers/staging/media/imx/imx-media-csi.c | 4 ++-- drivers/staging/media/imx/imx-media-vdic.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 7658b83466a7..5b09e11b1a0e 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -26,8 +26,8 @@ /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 4 /* multiple of 16 pixels */ diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index b1f84e0ac486..74f5de466d5d 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -35,8 +35,8 @@ * has not requested a planar format, we should allow 8 pixel * alignment at the source pad. */ -#define MIN_W_SINK 176 -#define MIN_H_SINK 144 +#define MIN_W_SINK 32 +#define MIN_H_SINK 32 #define MAX_W_SINK 4096 #define MAX_H_SINK 4096 #define W_ALIGN_SINK 3 /* multiple of 8 pixels */ diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d7e5b9ed27b8..d92fd804488e 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -33,8 +33,8 @@ * has not requested planar formats, we should allow 8 pixel * alignment. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 1 /* multiple of 2 pixels */ diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 303b5407fb64..879329f81f79 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -49,8 +49,8 @@ struct vdic_pipeline_ops { /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W_VDIC 968 #define MAX_H_VDIC 2048 #define W_ALIGN 4 /* multiple of 16 pixels */ -- cgit v1.2.3 From 94dc765602618fdab8bc4eaa96e3053929d8ff8f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:18 +0200 Subject: media: allegro: rework mbox handling Add a send/notify abstraction for the mailbox and separate the message handling in the driver from the code to read and write message to the mailbox. This untangles how mails are written into the MCU's SRAM and signaled to the MCU from the protocol between the driver and the firmware. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 145 ++++++++++++++--------- 1 file changed, 87 insertions(+), 58 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3ed66aae741d..11333f900302 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -105,9 +105,11 @@ struct allegro_buffer { struct list_head head; }; +struct allegro_dev; struct allegro_channel; struct allegro_mbox { + struct allegro_dev *dev; unsigned int head; unsigned int tail; unsigned int data; @@ -134,8 +136,8 @@ struct allegro_dev { struct completion init_complete; /* The mailbox interface */ - struct allegro_mbox mbox_command; - struct allegro_mbox mbox_status; + struct allegro_mbox *mbox_command; + struct allegro_mbox *mbox_status; /* * The downstream driver limits the users to 64 users, thus I can use @@ -583,12 +585,20 @@ static void allegro_free_buffer(struct allegro_dev *dev, * Mailbox interface to send messages to the MCU. */ -static int allegro_mbox_init(struct allegro_dev *dev, - struct allegro_mbox *mbox, - unsigned int base, size_t size) +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) { + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); if (!mbox) - return -EINVAL; + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; mbox->head = base; mbox->tail = base + 0x4; @@ -599,7 +609,7 @@ static int allegro_mbox_init(struct allegro_dev *dev, regmap_write(dev->sram, mbox->head, 0); regmap_write(dev->sram, mbox->tail, 0); - return 0; + return mbox; } static int allegro_mbox_write(struct allegro_dev *dev, @@ -713,9 +723,50 @@ static ssize_t allegro_mbox_read(struct allegro_dev *dev, return size; } -static void allegro_mcu_interrupt(struct allegro_dev *dev) +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) { - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); + struct allegro_dev *dev = mbox->dev; + struct mcu_msg_header *header = msg; + ssize_t size = sizeof(*header) + header->length; + int err; + + err = allegro_mbox_write(dev, mbox, msg, size); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + size = allegro_mbox_read(dev, mbox, msg, sizeof(*msg)); + if (size < 0) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(msg); } static void allegro_mcu_send_init(struct allegro_dev *dev, @@ -736,8 +787,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.l2_cache[1] = -1; msg.l2_cache[2] = -1; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); } static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) @@ -946,8 +996,7 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, fill_create_channel_param(channel, &msg.param); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -964,8 +1013,7 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, msg.channel_id = channel->mcu_channel_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -991,8 +1039,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, /* copied to mcu_msg_encode_frame_response */ msg.stream_id = stream_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1021,8 +1068,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.ep2 = 0x0; msg.ep2_v = to_mcu_addr(dev, msg.ep2); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1084,12 +1130,8 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, buffer++; } - err = allegro_mbox_write(dev, &dev->mbox_command, msg, size); - if (err) - goto out; - allegro_mcu_interrupt(dev); + err = allegro_mbox_send(dev->mbox_command, msg); -out: kfree(msg); return err; } @@ -1681,51 +1723,28 @@ allegro_handle_encode_frame(struct allegro_dev *dev, return 0; } -static int allegro_receive_message(struct allegro_dev *dev) +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) { - union mcu_msg_response *msg; - ssize_t size; - int err = 0; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - size = allegro_mbox_read(dev, &dev->mbox_status, msg, sizeof(*msg)); - if (size < sizeof(msg->header)) { - v4l2_err(&dev->v4l2_dev, - "invalid mbox message (%zd): must be at least %zu\n", - size, sizeof(msg->header)); - err = -EINVAL; - goto out; - } - switch (msg->header.type) { case MCU_MSG_TYPE_INIT: - err = allegro_handle_init(dev, &msg->init); + allegro_handle_init(dev, &msg->init); break; case MCU_MSG_TYPE_CREATE_CHANNEL: - err = allegro_handle_create_channel(dev, &msg->create_channel); + allegro_handle_create_channel(dev, &msg->create_channel); break; case MCU_MSG_TYPE_DESTROY_CHANNEL: - err = allegro_handle_destroy_channel(dev, - &msg->destroy_channel); + allegro_handle_destroy_channel(dev, &msg->destroy_channel); break; case MCU_MSG_TYPE_ENCODE_FRAME: - err = allegro_handle_encode_frame(dev, &msg->encode_frame); + allegro_handle_encode_frame(dev, &msg->encode_frame); break; default: v4l2_warn(&dev->v4l2_dev, "%s: unknown message %s\n", __func__, msg_type_name(msg->header.type)); - err = -EINVAL; break; } - -out: - kfree(msg); - - return err; } static irqreturn_t allegro_hardirq(int irq, void *data) @@ -1746,7 +1765,7 @@ static irqreturn_t allegro_irq_thread(int irq, void *data) { struct allegro_dev *dev = data; - allegro_receive_message(dev); + allegro_mbox_notify(dev->mbox_status); return IRQ_HANDLED; } @@ -1895,6 +1914,11 @@ static int allegro_mcu_reset(struct allegro_dev *dev) return allegro_mcu_wait_for_sleep(dev); } +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; @@ -2887,10 +2911,15 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, { int err; - allegro_mbox_init(dev, &dev->mbox_command, - info->mailbox_cmd, info->mailbox_size); - allegro_mbox_init(dev, &dev->mbox_status, - info->mailbox_status, info->mailbox_size); + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (!dev->mbox_command || !dev->mbox_status) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } allegro_mcu_enable_interrupts(dev); -- cgit v1.2.3 From e561f8e24c5ade18f97970c3034ac0f965370c3f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:19 +0200 Subject: media: allegro: rework read/write to mailbox Rework the functions that read and write the SRAM that is used to communicate with the MCU. As the functions will not operate on structs but on prepared binary buffers, make the buffer stride more explicit. Also, avoid any uses of struct mcu_msg_header to analyze messages in memory, because the header will be made independent of the binary representation in the mailbox. Instead explicitly access the mail size field in the mailbox. As at it, further reduce the dependency between the mailboxes and struct allegro_dev. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 110 ++++++++--------------- 1 file changed, 36 insertions(+), 74 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 11333f900302..dc2627df0eec 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -612,49 +612,34 @@ static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, return mbox; } -static int allegro_mbox_write(struct allegro_dev *dev, - struct allegro_mbox *mbox, void *src, size_t size) +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) { - struct mcu_msg_header *header = src; + struct regmap *sram = mbox->dev->sram; unsigned int tail; size_t size_no_wrap; int err = 0; + int stride = regmap_get_reg_stride(sram); if (!src) return -EINVAL; - if (size > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) too large for mailbox (%zu bytes)\n", - size, mbox->size); - return -EINVAL; - } - - if (header->length != size - sizeof(*header)) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %u bytes (expected %zu bytes)\n", - header->length, size - sizeof(*header)); + if (size > mbox->size) return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "write command message: type %s, body length %d\n", - msg_type_name(header->type), header->length); mutex_lock(&mbox->lock); - regmap_read(dev->sram, mbox->tail, &tail); + regmap_read(sram, mbox->tail, &tail); if (tail > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n", - tail, mbox->size); err = -EIO; goto out; } size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4); - regmap_bulk_write(dev->sram, mbox->data, - src + size_no_wrap, (size - size_no_wrap) / 4); - regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); out: mutex_unlock(&mbox->lock); @@ -662,40 +647,32 @@ out: return err; } -static ssize_t allegro_mbox_read(struct allegro_dev *dev, - struct allegro_mbox *mbox, - void *dst, size_t nbyte) +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) { - struct mcu_msg_header *header; + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; unsigned int head; ssize_t size; size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); - regmap_read(dev->sram, mbox->head, &head); - if (head > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n", - head, mbox->size); + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) return -EIO; - } /* Assume that the header does not wrap. */ - regmap_bulk_read(dev->sram, mbox->data + head, - dst, sizeof(*header) / 4); - header = dst; + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %zu bytes (maximum %zu bytes)\n", - header->length + sizeof(*header), mbox->size); + if (size > mbox->size || size & 0x3) return -EIO; - } - if (size > nbyte) { - v4l2_err(&dev->v4l2_dev, - "destination buffer too small: %zu bytes (need %zu bytes)\n", - nbyte, size); + if (size > nbyte) return -EINVAL; - } /* * The message might wrap within the mailbox. If the message does not @@ -708,17 +685,14 @@ static ssize_t allegro_mbox_read(struct allegro_dev *dev, */ body_no_wrap = min((size_t)header->length, (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header), - dst + sizeof(*header), body_no_wrap / 4); - regmap_bulk_read(dev->sram, mbox->data, - dst + sizeof(*header) + body_no_wrap, - (header->length - body_no_wrap) / 4); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); - regmap_write(dev->sram, mbox->head, (head + size) % mbox->size); - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "read status message: type %s, body length %d\n", - msg_type_name(header->type), header->length); + regmap_write(sram, mbox->head, (head + size) % mbox->size); return size; } @@ -735,7 +709,7 @@ static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) ssize_t size = sizeof(*header) + header->length; int err; - err = allegro_mbox_write(dev, mbox, msg, size); + err = allegro_mbox_write(mbox, msg, size); if (err) goto out; @@ -759,7 +733,7 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) if (!msg) return; - size = allegro_mbox_read(dev, mbox, msg, sizeof(*msg)); + size = allegro_mbox_read(mbox, (u32 *)msg, sizeof(*msg)); if (size < 0) goto out; @@ -1604,12 +1578,6 @@ allegro_handle_create_channel(struct allegro_dev *dev, struct allegro_channel *channel; int err = 0; - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); - channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -1703,12 +1671,6 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, -- cgit v1.2.3 From 3de16839669f60b6049734e3a9f553a2a958c4b5 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:20 +0200 Subject: media: allegro: add explicit mail encoding and decoding The message format in the mailboxes differ between firmware versions. Therefore, it is necessary to decouple the mailbox format of the driver from the message format of the firmware. This allows to keep a consistent message format in the driver while still supporting various firmware versions. Add an intermediate step to encode and decode message before writing the mails to the mailboxes. On the other hand, this allows to handle optional fields in the messages, which is required for advanced features of the encoder and was not possible until now. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 28 +- drivers/staging/media/allegro-dvt/allegro-mail.c | 364 +++++++++++++++++++++++ drivers/staging/media/allegro-dvt/allegro-mail.h | 3 + 3 files changed, 391 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index dc2627df0eec..6334b351ee3b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -705,11 +705,20 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) { struct allegro_dev *dev = mbox->dev; - struct mcu_msg_header *header = msg; - ssize_t size = sizeof(*header) + header->length; + ssize_t size; int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); - err = allegro_mbox_write(mbox, msg, size); + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); if (err) goto out; @@ -728,18 +737,29 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) struct allegro_dev *dev = mbox->dev; union mcu_msg_response *msg; ssize_t size; + u32 *tmp; + int err; msg = kmalloc(sizeof(*msg), GFP_KERNEL); if (!msg) return; - size = allegro_mbox_read(mbox, (u32 *)msg, sizeof(*msg)); + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); if (size < 0) goto out; + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + allegro_handle_message(dev, msg); out: + kfree(tmp); kfree(msg); } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index df0d8d26a6fb..c78367d36e2e 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -6,7 +6,9 @@ * Allegro VCU firmware. */ +#include #include +#include #include "allegro-mail.h" @@ -35,3 +37,365 @@ const char *msg_type_name(enum mcu_msg_type type) } } EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) +{ + unsigned int i = 0; + + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | + FIELD_PREP(GENMASK(15, 0), param->width); + dst[i++] = param->format; + dst[i++] = param->colorspace; + dst[i++] = param->src_mode; + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->codec) | + FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | + FIELD_PREP(GENMASK(7, 0), param->profile); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | + FIELD_PREP(GENMASK(15, 0), param->level); + dst[i++] = param->sps_param; + dst[i++] = param->pps_param; + dst[i++] = param->enc_option; + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + dst[i++] = param->rate_control_option; + + dst[i++] = param->gop_ctrl_mode; + dst[i++] = param->freq_idr; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + dst[i++] = param->unknown41; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + struct create_channel_param *param = &msg->param; + ssize_t size = 0; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + size = allegro_encode_channel_config(&dst[i], param); + i += size / sizeof(*dst); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = (msg->header.length - 4) / sizeof(*buffer); + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->stream_id); + dst[i++] = upper_32_bits(msg->stream_id); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + dst[i++] = msg->reserved; + dst[i++] = msg->encoding_options; + dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | + FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + dst[i++] = lower_32_bits(msg->user_param); + dst[i++] = upper_32_bits(msg->user_param); + dst[i++] = lower_32_bits(msg->src_handle); + dst[i++] = upper_32_bits(msg->src_handle); + dst[i++] = msg->request_options; + dst[i++] = msg->src_y; + dst[i++] = msg->src_uv; + dst[i++] = msg->stride; + dst[i++] = msg->ep2; + dst[i++] = lower_32_bits(msg->ep2_v); + dst[i++] = upper_32_bits(msg->ep2_v); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->reserved0 = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + msg->user_id = src[i++]; + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->pps_param = src[i++]; + msg->int_buffers_count = src[i++]; + msg->int_buffers_size = src[i++]; + msg->rec_buffers_count = src[i++]; + msg->rec_buffers_size = src[i++]; + msg->reserved = src[i++]; + msg->error_code = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) +{ + unsigned int i = 0; + unsigned int j; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + + msg->stream_id = src[i++]; + msg->stream_id |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + enum mcu_msg_type type = header->type; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = (struct mcu_msg_header *)src; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 17db665f8e1e..457caf50ebe6 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -264,4 +264,7 @@ union mcu_msg_response { struct mcu_msg_encode_frame_response encode_frame; }; +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + #endif -- cgit v1.2.3 From 53c200ff49636de598b2ef5689d548c774b0373a Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:21 +0200 Subject: media: allegro: add field for number of buffers When pushing the buffers for the intermediate and reference frames to the MCU, the driver relied on the message size to calculate the number of buffers. As it is not necessary anymore to keep the messages binary compatible to the firmware, we can just explicitly write the number of buffers into the message. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 1 + drivers/staging/media/allegro-dvt/allegro-mail.c | 2 +- drivers/staging/media/allegro-dvt/allegro-mail.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 6334b351ee3b..6745a5fa1167 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -1115,6 +1115,7 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, msg->header.length = size - sizeof(msg->header); msg->header.type = type; msg->channel_id = channel->mcu_channel_id; + msg->num_buffers = num_buffers; buffer = msg->buffer; list_for_each_entry(al_buffer, list, head) { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index c78367d36e2e..ba1c3bc587c6 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -156,7 +156,7 @@ allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) { unsigned int i = 0; struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = (msg->header.length - 4) / sizeof(*buffer); + unsigned int num_buffers = msg->num_buffers; unsigned int j; dst[i++] = msg->channel_id; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 457caf50ebe6..14b54eb52488 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -169,6 +169,7 @@ struct mcu_msg_push_buffers_internal_buffer { struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; + size_t num_buffers; struct mcu_msg_push_buffers_internal_buffer buffer[]; } __attribute__ ((__packed__)); -- cgit v1.2.3 From fc1c13deb1f95467cf9819ef57157b542efcb901 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:22 +0200 Subject: media: allegro: don't pack MCU messages The messages are now explicitly converted from the struct to the binary representation used by the firmware. Therefore, there is no need to keep the structs packed anymore. Drop the attribute and avoid confusion why the mails are packed. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-mail.h | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 14b54eb52488..ae81e94a2f2c 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -25,7 +25,7 @@ const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { u16 length; /* length of the body in bytes */ u16 type; -} __attribute__ ((__packed__)); +}; struct mcu_msg_init_request { struct mcu_msg_header header; @@ -33,12 +33,12 @@ struct mcu_msg_init_request { u32 suballoc_dma; u32 suballoc_size; s32 l2_cache[3]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_init_response { struct mcu_msg_header header; u32 reserved0; -} __attribute__ ((__packed__)); +}; struct create_channel_param { u16 width; @@ -127,13 +127,13 @@ struct create_channel_param { u32 subframe_latency; u32 lda_control_mode; u32 unknown41; -} __attribute__ ((__packed__)); +}; struct mcu_msg_create_channel { struct mcu_msg_header header; u32 user_id; struct create_channel_param param; -} __attribute__ ((__packed__)); +}; struct mcu_msg_create_channel_response { struct mcu_msg_header header; @@ -148,30 +148,30 @@ struct mcu_msg_create_channel_response { u32 rec_buffers_size; u32 reserved; u32 error_code; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel_response { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal_buffer { u32 dma_addr; u32 mcu_addr; u32 size; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; size_t num_buffers; struct mcu_msg_push_buffers_internal_buffer buffer[]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_put_stream_buffer { struct mcu_msg_header header; @@ -181,7 +181,7 @@ struct mcu_msg_put_stream_buffer { u32 size; u32 offset; u64 stream_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame { struct mcu_msg_header header; @@ -214,7 +214,7 @@ struct mcu_msg_encode_frame { u32 stride; u32 ep2; u64 ep2_v; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame_response { struct mcu_msg_header header; @@ -255,7 +255,7 @@ struct mcu_msg_encode_frame_response { u16 pps_qp; u16 reserved1; u32 reserved2; -} __attribute__ ((__packed__)); +}; union mcu_msg_response { struct mcu_msg_header header; -- cgit v1.2.3 From 62ed97df7dc1bbb564fcd1c4f795aaa74245c63f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:23 +0200 Subject: media: allegro: support handling firmware dependent values Like the message format, also the identifiers in the messages differ between firmware versions. This especially affects the identifier for the codec that shall be used. As the messages used by the driver are now independent from the firmware, we can use the values defined by V4L2 as identifiers in the messages. Convert the V4L2 codec format to the respective firmware value when encoding the messages to binary format instead beforehand. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 11 +---------- drivers/staging/media/allegro-dvt/allegro-mail.c | 15 ++++++++++++++- drivers/staging/media/allegro-dvt/allegro-mail.h | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 6745a5fa1167..1bbb44140a6c 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -812,15 +812,6 @@ static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) } } -static s8 v4l2_pixelformat_to_mcu_codec(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; - } -} - static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) { switch (profile) { @@ -919,7 +910,7 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->src_mode = 0x0; param->profile = v4l2_profile_to_mcu_profile(channel->profile); param->constraint_set_flags = BIT(1); - param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + param->codec = channel->codec; param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; param->sps_param = BIT(20) | 0x4a; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index ba1c3bc587c6..ce183bd2495b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "allegro-mail.h" @@ -53,17 +54,29 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) return i * sizeof(*dst); } +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + u32 pixelformat = param->codec; + + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } +} + static ssize_t allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; + unsigned int codec = settings_get_mcu_codec(param); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | FIELD_PREP(GENMASK(15, 0), param->width); dst[i++] = param->format; dst[i++] = param->colorspace; dst[i++] = param->src_mode; - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->codec) | + dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | FIELD_PREP(GENMASK(7, 0), param->profile); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index ae81e94a2f2c..a92a27e270b5 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -48,7 +48,7 @@ struct create_channel_param { u32 src_mode; u8 profile; u16 constraint_set_flags; - s8 codec; + u32 codec; u16 level; u16 tier; u32 sps_param; -- cgit v1.2.3 From d30e841216411cb95ffbb56252f8431fd752c54d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:24 +0200 Subject: media: allegro: encode bit fields separately Even bits in bitfields do not keep their position, but move around or move to other bitfields. Therefore, the driver has to handle bitfields differently depending on the firmware version. Create separate fields for the options that have been in bitfields and handle the bitfields when encoding the message. As a side effect, this makes the messages a bit more readable. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 15 ++++++++--- drivers/staging/media/allegro-dvt/allegro-mail.c | 22 +++++++++++++--- drivers/staging/media/allegro-dvt/allegro-mail.h | 32 ++++++------------------ 3 files changed, 38 insertions(+), 31 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 1bbb44140a6c..e6d64151e4fe 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -913,10 +913,17 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->codec = channel->codec; param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; - param->sps_param = BIT(20) | 0x4a; - param->pps_param = BIT(2); - param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | - AL_OPT_LF_X_SLICE | AL_OPT_LF; + + param->log2_max_poc = 10; + param->log2_max_frame_num = 4; + param->temporal_mvp_enable = 1; + + param->dbf_ovr_en = 1; + param->rdo_cost_mode = 1; + param->lf = 1; + param->lf_x_tile = 1; + param->lf_x_slice = 1; + param->beta_offset = -1; param->tc_offset = -1; param->num_slices = 1; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index ce183bd2495b..7121f128aff3 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -69,6 +69,7 @@ static ssize_t allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; + u32 val; unsigned int codec = settings_get_mcu_codec(param); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | @@ -81,9 +82,24 @@ allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) FIELD_PREP(GENMASK(7, 0), param->profile); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | FIELD_PREP(GENMASK(15, 0), param->level); - dst[i++] = param->sps_param; - dst[i++] = param->pps_param; - dst[i++] = param->enc_option; + + val = 0; + val |= param->temporal_mvp_enable ? BIT(20) : 0; + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | + FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | FIELD_PREP(GENMASK(7, 0), param->tc_offset); dst[i++] = param->unknown11; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index a92a27e270b5..239fd8a20a69 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -51,30 +51,14 @@ struct create_channel_param { u32 codec; u16 level; u16 tier; - u32 sps_param; - u32 pps_param; - - u32 enc_option; -#define AL_OPT_WPP BIT(0) -#define AL_OPT_TILE BIT(1) -#define AL_OPT_LF BIT(2) -#define AL_OPT_LF_X_SLICE BIT(3) -#define AL_OPT_LF_X_TILE BIT(4) -#define AL_OPT_SCL_LST BIT(5) -#define AL_OPT_CONST_INTRA_PRED BIT(6) -#define AL_OPT_QP_TAB_RELATIVE BIT(7) -#define AL_OPT_FIX_PREDICTOR BIT(8) -#define AL_OPT_CUSTOM_LDA BIT(9) -#define AL_OPT_ENABLE_AUTO_QP BIT(10) -#define AL_OPT_ADAPT_AUTO_QP BIT(11) -#define AL_OPT_TRANSFO_SKIP BIT(13) -#define AL_OPT_FORCE_REC BIT(15) -#define AL_OPT_FORCE_MV_OUT BIT(16) -#define AL_OPT_FORCE_MV_CLIP BIT(17) -#define AL_OPT_LOWLAT_SYNC BIT(18) -#define AL_OPT_LOWLAT_INT BIT(19) -#define AL_OPT_RDO_COST_MODE BIT(20) - + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 dbf_ovr_en; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; s8 beta_offset; s8 tc_offset; u16 reserved10; -- cgit v1.2.3 From c4756d8774159ef1a9a204d656589c886fcefc5b Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:25 +0200 Subject: media: allegro: add config blob for channel Firmware versions >= 2019.2 do not configure the channel via the mailbox interface anymore, but use a separate chunk of memory that is only referenced by the message. As the configuration must be in a format that is understood by the firmware and this format can change between firmware versions, handle it similar to the message and treat is as a blob. In order to support both methods in the driver, always use a separate blob for the channel configuration and copy that blob into the mailbox if the firmware requires it and otherwise reference it. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 20 +++++++++++++++++++- drivers/staging/media/allegro-dvt/allegro-mail.c | 18 ++++++++++++------ drivers/staging/media/allegro-dvt/allegro-mail.h | 9 ++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index e6d64151e4fe..51e85cad9941 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -206,6 +206,8 @@ struct allegro_channel { unsigned int cpb_size; unsigned int gop_size; + struct allegro_buffer config_blob; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -978,6 +980,14 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, struct allegro_channel *channel) { struct mcu_msg_create_channel msg; + struct allegro_buffer *blob = &channel->config_blob; + struct create_channel_param param; + size_t size; + + memset(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + size = allegro_encode_config_blob(blob->vaddr, ¶m); memset(&msg, 0, sizeof(msg)); @@ -986,7 +996,9 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, msg.user_id = channel->user_id; - fill_create_channel_param(channel, &msg.param); + msg.blob = blob->vaddr; + msg.blob_size = size; + msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); allegro_mbox_send(dev->mbox_command, &msg); @@ -1596,6 +1608,7 @@ allegro_handle_create_channel(struct allegro_dev *dev, { struct allegro_channel *channel; int err = 0; + struct create_channel_param param; channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { @@ -1621,6 +1634,11 @@ allegro_handle_create_channel(struct allegro_dev *dev, "user %d: channel has channel id %d\n", channel->user_id, channel->mcu_channel_id); + err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + v4l2_dbg(1, debug, &dev->v4l2_dev, "channel %d: intermediate buffers: %d x %d bytes\n", channel->mcu_channel_id, diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index 7121f128aff3..c55b9339f9c6 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "allegro-mail.h" @@ -65,8 +66,8 @@ static inline u32 settings_get_mcu_codec(struct create_channel_param *param) } } -static ssize_t -allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) +ssize_t +allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; u32 val; @@ -158,18 +159,23 @@ allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) static ssize_t allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) { - struct create_channel_param *param = &msg->param; - ssize_t size = 0; unsigned int i = 0; dst[i++] = msg->user_id; - size = allegro_encode_channel_config(&dst[i], param); - i += size / sizeof(*dst); + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); return i * sizeof(*dst); } +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + return 0; +} + static ssize_t allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 239fd8a20a69..07ed0a8d3de3 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -116,7 +116,9 @@ struct create_channel_param { struct mcu_msg_create_channel { struct mcu_msg_header header; u32 user_id; - struct create_channel_param param; + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; }; struct mcu_msg_create_channel_response { @@ -249,6 +251,11 @@ union mcu_msg_response { struct mcu_msg_encode_frame_response encode_frame; }; +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + int allegro_decode_mail(void *msg, u32 *src); ssize_t allegro_encode_mail(u32 *dst, void *msg); -- cgit v1.2.3 From c7ce107f9d899a15e4b9c24c4839abdbef88a063 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:26 +0200 Subject: media: allegro: set num_ref_idx using response of configured channels The firmware decides how many reference frames shall be used und returns this information via the config blob. In order to set the number of reference frames in the PPS, the driver has to read these values from the config blob after the channel has been created in the firmware. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 10 ++++++++-- drivers/staging/media/allegro-dvt/allegro-mail.c | 6 +++++- drivers/staging/media/allegro-dvt/allegro-mail.h | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 51e85cad9941..07deb5ba13dc 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -208,6 +208,9 @@ struct allegro_channel { struct allegro_buffer config_blob; + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -1336,8 +1339,8 @@ static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, pps->entropy_coding_mode_flag = 0; pps->bottom_field_pic_order_in_frame_present_flag = 0; pps->num_slice_groups_minus1 = 0; - pps->num_ref_idx_l0_default_active_minus1 = 2; - pps->num_ref_idx_l1_default_active_minus1 = 2; + pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; + pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; pps->weighted_pred_flag = 0; pps->weighted_bipred_idc = 0; pps->pic_init_qp_minus26 = 0; @@ -1639,6 +1642,9 @@ allegro_handle_create_channel(struct allegro_dev *dev, if (err) goto out; + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "channel %d: intermediate buffers: %d x %d bytes\n", channel->mcu_channel_id, diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index c55b9339f9c6..bb15de080431 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -173,6 +173,9 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param, struct mcu_msg_create_channel_response *msg, u32 *src) { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + return 0; } @@ -273,7 +276,8 @@ allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, msg->user_id = src[i++]; msg->options = src[i++]; msg->num_core = src[i++]; - msg->pps_param = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); msg->int_buffers_count = src[i++]; msg->int_buffers_size = src[i++]; msg->rec_buffers_count = src[i++]; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 07ed0a8d3de3..a4d829f6f99d 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -55,6 +55,8 @@ struct create_channel_param { u32 log2_max_frame_num; u32 temporal_mvp_enable; u32 dbf_ovr_en; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; u32 rdo_cost_mode; u32 lf; u32 lf_x_tile; @@ -127,7 +129,8 @@ struct mcu_msg_create_channel_response { u32 user_id; u32 options; u32 num_core; - u32 pps_param; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; u32 int_buffers_count; u32 int_buffers_size; u32 rec_buffers_count; -- cgit v1.2.3 From d8fefda89ee292b2871ce523227e5a49d4afc6ea Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:27 +0200 Subject: media: allegro: drop length field from message header The length of the message will be determined when the message is encoded. Writing the size of the struct into the message in the driver won't be the actual length of the message that is send to the firmware. Therefore, drop the length field from the message. Since the header is the same for all response messages, it does not make sense to read the header in each decoding function, but we can simply decode it once before dispatching to the respective functions. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 6 ------ drivers/staging/media/allegro-dvt/allegro-mail.c | 13 ++++--------- drivers/staging/media/allegro-dvt/allegro-mail.h | 3 +-- 3 files changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 07deb5ba13dc..85d2c45be9d2 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -776,7 +776,6 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); @@ -995,7 +994,6 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.user_id = channel->user_id; @@ -1016,7 +1014,6 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; @@ -1036,7 +1033,6 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; msg.dma_addr = to_codec_addr(dev, paddr); @@ -1061,7 +1057,6 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; @@ -1125,7 +1120,6 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, if (!msg) return -ENOMEM; - msg->header.length = size - sizeof(msg->header); msg->header.type = type; msg->channel_id = channel->mcu_channel_id; msg->num_buffers = num_buffers; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index bb15de080431..da83aa85c873 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -257,8 +257,6 @@ allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->reserved0 = src[i++]; return i * sizeof(*src); @@ -270,8 +268,6 @@ allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; msg->user_id = src[i++]; msg->options = src[i++]; @@ -294,8 +290,6 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; return i * sizeof(*src); @@ -307,8 +301,6 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) unsigned int i = 0; unsigned int j; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; msg->stream_id = src[i++]; @@ -418,7 +410,10 @@ int allegro_decode_mail(void *msg, u32 *src) if (!src || !msg) return -EINVAL; - header = (struct mcu_msg_header *)src; + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; switch (header->type) { case MCU_MSG_TYPE_INIT: allegro_dec_init(msg, src); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index a4d829f6f99d..397622973c19 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -23,8 +23,7 @@ enum mcu_msg_type { const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { - u16 length; /* length of the body in bytes */ - u16 type; + enum mcu_msg_type type; }; struct mcu_msg_init_request { -- cgit v1.2.3 From 11146a758317f88034895dd778bf4a2ccac47303 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:28 +0200 Subject: media: allegro: add a version field to mcu messages In order to distinguish the message format that is expected by the firmware, add a version field to the message header. This allows to encode and decode the messages for the version of the firmware that was loaded by the driver. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 22 +++++++++++++++++----- drivers/staging/media/allegro-dvt/allegro-mail.h | 6 ++++++ 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 85d2c45be9d2..ada21ebf93bb 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -130,6 +130,7 @@ struct allegro_dev { struct regmap *regmap; struct regmap *sram; + const struct fw_info *fw_info; struct allegro_buffer firmware; struct allegro_buffer suballocator; @@ -277,6 +278,7 @@ struct fw_info { unsigned int mailbox_cmd; unsigned int mailbox_status; size_t mailbox_size; + enum mcu_msg_version mailbox_version; size_t suballocator_size; }; @@ -288,6 +290,7 @@ static const struct fw_info supported_firmware[] = { .mailbox_cmd = 0x7800, .mailbox_status = 0x7c00, .mailbox_size = 0x400 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2018_2, .suballocator_size = SZ_16M, }, }; @@ -749,6 +752,8 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) if (!msg) return; + msg->header.version = dev->fw_info->mailbox_version; + tmp = kmalloc(mbox->size, GFP_KERNEL); if (!tmp) goto out; @@ -776,6 +781,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_INIT; + msg.header.version = dev->fw_info->mailbox_version; msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); @@ -989,11 +995,13 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, memset(¶m, 0, sizeof(param)); fill_create_channel_param(channel, ¶m); allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; size = allegro_encode_config_blob(blob->vaddr, ¶m); memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; msg.user_id = channel->user_id; @@ -1014,6 +1022,7 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; @@ -1033,6 +1042,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.dma_addr = to_codec_addr(dev, paddr); @@ -1057,6 +1067,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; @@ -1121,6 +1132,8 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, return -ENOMEM; msg->header.type = type; + msg->header.version = dev->fw_info->mailbox_version; + msg->channel_id = channel->mcu_channel_id; msg->num_buffers = num_buffers; @@ -2988,7 +3001,6 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) const char *fw_codec_name = "al5e.fw"; const struct firmware *fw_codec; int err; - const struct fw_info *info; if (!fw) return; @@ -2999,14 +3011,14 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) if (err) goto err_release_firmware; - info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!info) { + dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!dev->fw_info) { v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); goto err_release_firmware_codec; } v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", info->version); + "using mcu firmware version '%s'\n", dev->fw_info->version); /* Ensure that the mcu is sleeping at the reset vector */ err = allegro_mcu_reset(dev); @@ -3018,7 +3030,7 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) allegro_copy_firmware(dev, fw->data, fw->size); allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - err = allegro_mcu_hw_init(dev, info); + err = allegro_mcu_hw_init(dev, dev->fw_info); if (err) { v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); goto err_free_fw_codec; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 397622973c19..c095dbfcf104 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -20,10 +20,15 @@ enum mcu_msg_type { MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, }; +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, +}; + const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { enum mcu_msg_type type; + enum mcu_msg_version version; }; struct mcu_msg_init_request { @@ -40,6 +45,7 @@ struct mcu_msg_init_response { }; struct create_channel_param { + enum mcu_msg_version version; u16 width; u16 height; u32 format; -- cgit v1.2.3 From 42e50290d79475183a728d0c1d07020061f8838b Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:29 +0200 Subject: media: allegro: add support for allegro firmware 2019.2 Encode messages as necessary for the firmware 2019.2 and, thus, support the more recent firmware version in the driver, too. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 27 +++- drivers/staging/media/allegro-dvt/allegro-mail.c | 163 +++++++++++++++++++---- drivers/staging/media/allegro-dvt/allegro-mail.h | 24 ++++ 3 files changed, 186 insertions(+), 28 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index ada21ebf93bb..61beae1fca36 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -292,6 +292,15 @@ static const struct fw_info supported_firmware[] = { .mailbox_size = 0x400 - 0x8, .mailbox_version = MCU_MSG_VERSION_2018_2, .suballocator_size = SZ_16M, + }, { + .id = 14680, + .id_codec = 126572, + .version = "v2019.2", + .mailbox_cmd = 0x7000, + .mailbox_status = 0x7800, + .mailbox_size = 0x800 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2019_2, + .suballocator_size = SZ_32M, }, }; @@ -930,10 +939,13 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->dbf_ovr_en = 1; param->rdo_cost_mode = 1; + param->custom_lda = 1; param->lf = 1; param->lf_x_tile = 1; param->lf_x_slice = 1; + param->src_bit_depth = 8; + param->beta_offset = -1; param->tc_offset = -1; param->num_slices = 1; @@ -974,13 +986,26 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->golden_ref_frequency = 10; param->rate_control_option = 0x00000000; - param->gop_ctrl_mode = 0x00000000; + param->num_pixel = channel->width + channel->height; + param->max_psnr = 4200; + param->max_pixel_value = 255; + + param->gop_ctrl_mode = 0x00000002; param->freq_idr = channel->gop_size; param->freq_lt = 0; param->gdr_mode = 0x00000000; param->gop_length = channel->gop_size; param->subframe_latency = 0x00000000; + param->lda_factors[0] = 51; + param->lda_factors[1] = 90; + param->lda_factors[2] = 151; + param->lda_factors[3] = 151; + param->lda_factors[4] = 151; + param->lda_factors[5] = 151; + + param->max_num_merge_cand = 5; + return 0; } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index da83aa85c873..4ac65de12463 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -44,6 +44,7 @@ static ssize_t allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) { unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; dst[i++] = msg->reserved0; dst[i++] = msg->suballoc_dma; @@ -51,33 +52,55 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) dst[i++] = msg->l2_cache[0]; dst[i++] = msg->l2_cache[1]; dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } return i * sizeof(*dst); } static inline u32 settings_get_mcu_codec(struct create_channel_param *param) { + enum mcu_msg_version version = param->version; u32 pixelformat = param->codec; - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 0; + } } } ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) { + enum mcu_msg_version version = param->version; unsigned int i = 0; + unsigned int j = 0; u32 val; unsigned int codec = settings_get_mcu_codec(param); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->layer_id; dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | FIELD_PREP(GENMASK(15, 0), param->width); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->videomode; dst[i++] = param->format; - dst[i++] = param->colorspace; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->colorspace; dst[i++] = param->src_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->src_bit_depth; dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | FIELD_PREP(GENMASK(7, 0), param->profile); @@ -94,19 +117,31 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) val |= param->dbf_ovr_en ? BIT(2) : 0; dst[i++] = val; - val = 0; - val |= param->lf ? BIT(2) : 0; - val |= param->lf_x_tile ? BIT(3) : 0; - val |= param->lf_x_slice ? BIT(4) : 0; - val |= param->rdo_cost_mode ? BIT(20) : 0; - dst[i++] = val; + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | FIELD_PREP(GENMASK(7, 0), param->tc_offset); dst[i++] = param->unknown11; dst[i++] = param->unknown12; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | - FIELD_PREP(GENMASK(15, 0), param->num_slices); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); dst[i++] = param->prefetch_mem_offset; dst[i++] = param->prefetch_mem_size; dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | @@ -139,19 +174,51 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) FIELD_PREP(GENMASK(15, 0), param->pb_delta); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | FIELD_PREP(GENMASK(15, 0), param->golden_delta); - dst[i++] = param->rate_control_option; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; - dst[i++] = param->gop_ctrl_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; dst[i++] = param->freq_lt; dst[i++] = param->gdr_mode; - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; dst[i++] = param->subframe_latency; dst[i++] = param->lda_control_mode; - dst[i++] = param->unknown41; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } return i * sizeof(*dst); } @@ -159,12 +226,20 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) static ssize_t allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; dst[i++] = msg->user_id; - memcpy(&dst[i], msg->blob, msg->blob_size); - i += msg->blob_size / sizeof(*dst); + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; return i * sizeof(*dst); } @@ -173,8 +248,15 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param, struct mcu_msg_create_channel_response *msg, u32 *src) { - param->num_ref_idx_l0 = msg->num_ref_idx_l0; - param->num_ref_idx_l1 = msg->num_ref_idx_l1; + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } return 0; } @@ -229,6 +311,7 @@ allegro_enc_put_stream_buffer(u32 *dst, static ssize_t allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; dst[i++] = msg->channel_id; @@ -237,6 +320,14 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) dst[i++] = msg->encoding_options; dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + } + dst[i++] = lower_32_bits(msg->user_param); dst[i++] = upper_32_bits(msg->user_param); dst[i++] = lower_32_bits(msg->src_handle); @@ -244,7 +335,11 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) dst[i++] = msg->request_options; dst[i++] = msg->src_y; dst[i++] = msg->src_uv; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->is_10_bit; dst[i++] = msg->stride; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->format; dst[i++] = msg->ep2; dst[i++] = lower_32_bits(msg->ep2_v); dst[i++] = upper_32_bits(msg->ep2_v); @@ -266,14 +361,21 @@ static ssize_t allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, u32 *src) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; msg->channel_id = src[i++]; msg->user_id = src[i++]; - msg->options = src[i++]; - msg->num_core = src[i++]; - msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); - msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + /* + * Version >= MCU_MSG_VERSION_2019_2 is handled in + * allegro_decode_config_blob(). + */ + if (version < MCU_MSG_VERSION_2019_2) { + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + } msg->int_buffers_count = src[i++]; msg->int_buffers_size = src[i++]; msg->rec_buffers_count = src[i++]; @@ -298,6 +400,7 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, static ssize_t allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; unsigned int j; @@ -341,6 +444,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } return i * sizeof(*src); } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index c095dbfcf104..486ecb12b098 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -22,6 +22,7 @@ enum mcu_msg_type { enum mcu_msg_version { MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, }; const char *msg_type_name(enum mcu_msg_type type); @@ -46,11 +47,14 @@ struct mcu_msg_init_response { struct create_channel_param { enum mcu_msg_version version; + u32 layer_id; u16 width; u16 height; + u32 videomode; u32 format; u32 colorspace; u32 src_mode; + u32 src_bit_depth; u8 profile; u16 constraint_set_flags; u32 codec; @@ -59,9 +63,11 @@ struct create_channel_param { u32 log2_max_poc; u32 log2_max_frame_num; u32 temporal_mvp_enable; + u32 enable_reordering; u32 dbf_ovr_en; u32 num_ref_idx_l0; u32 num_ref_idx_l1; + u32 custom_lda; u32 rdo_cost_mode; u32 lf; u32 lf_x_tile; @@ -105,6 +111,10 @@ struct create_channel_param { u16 golden_delta; u16 golden_ref_frequency; u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; /* gop param */ u32 gop_ctrl_mode; @@ -114,10 +124,16 @@ struct create_channel_param { u16 gop_length; u8 num_b; u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; u32 subframe_latency; u32 lda_control_mode; u32 unknown41; + + u32 lda_factors[6]; + + u32 max_num_merge_cand; }; struct mcu_msg_create_channel { @@ -126,6 +142,7 @@ struct mcu_msg_create_channel { u32 *blob; size_t blob_size; u32 blob_mcu_addr; + u32 ep1_addr; }; struct mcu_msg_create_channel_response { @@ -203,9 +220,12 @@ struct mcu_msg_encode_frame { /* u32 scene_change_delay (optional) */ /* rate control param (optional) */ /* gop param (optional) */ + /* dynamic resolution params (optional) */ u32 src_y; u32 src_uv; + u32 is_10_bit; u32 stride; + u32 format; u32 ep2; u64 ep2_v; }; @@ -249,6 +269,10 @@ struct mcu_msg_encode_frame_response { u16 pps_qp; u16 reserved1; u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; }; union mcu_msg_response { -- cgit v1.2.3 From aa86e0bbf2ee3fe338798af78f02b5e6088b50c1 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:29 +0200 Subject: media: staging: rkisp1: cap: don't set next buffer from rkisp1_vb2_buf_queue The function 'rkisp1_vb2_buf_queue' sets the next buffer directly in case the capture is already streaming but no frame yet arrived from the sensor. This is an optimization that tries to avoid dropping a frame. The call atomic_read(&cap->rkisp1->isp.frame_sequence) is used to check if a frame arrived. Reading the 'frame_sequence' should be avoided outside irq handlers to avoid race conditions. This patch removes this optimization. Dropping of the first frames can be avoided if userspace queues the buffers before start streaming. If userspace starts queueing buffers only after calling 'streamon' he risks frame drops anyway. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 793ec884c894..572b0949c81f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -743,18 +743,7 @@ static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) ispbuf->buff_addr[RKISP1_PLANE_CB]); spin_lock_irqsave(&cap->buf.lock, flags); - - /* - * If there's no next buffer assigned, queue this buffer directly - * as the next buffer, and update the memory interface. - */ - if (cap->is_streaming && !cap->buf.next && - atomic_read(&cap->rkisp1->isp.frame_sequence) == -1) { - cap->buf.next = ispbuf; - rkisp1_set_next_buf(cap); - } else { - list_add_tail(&ispbuf->queue, &cap->buf.queue); - } + list_add_tail(&ispbuf->queue, &cap->buf.queue); spin_unlock_irqrestore(&cap->buf.lock, flags); } -- cgit v1.2.3 From 454748e3b3529218a50920e643dab6f149ab2639 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:30 +0200 Subject: media: staging: rkisp1: cap: protect buf.curr and buf.next with buf.lock The spinlock buf.lock should protect access to the buffers. This includes the buffers in buf.queue and also buf.curr and buf.next. The function 'rkisp1_set_next_buf' access buf.next therefore it should also be protected with the lock. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 572b0949c81f..fa3eaeac2a00 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -617,10 +617,11 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) static void rkisp1_handle_buffer(struct rkisp1_capture *cap) { struct rkisp1_isp *isp = &cap->rkisp1->isp; - struct rkisp1_buffer *curr_buf = cap->buf.curr; + struct rkisp1_buffer *curr_buf; unsigned long flags; spin_lock_irqsave(&cap->buf.lock, flags); + curr_buf = cap->buf.curr; if (curr_buf) { curr_buf->vb.sequence = atomic_read(&isp->frame_sequence); @@ -640,9 +641,9 @@ static void rkisp1_handle_buffer(struct rkisp1_capture *cap) queue); list_del(&cap->buf.next->queue); } - spin_unlock_irqrestore(&cap->buf.lock, flags); rkisp1_set_next_buf(cap); + spin_unlock_irqrestore(&cap->buf.lock, flags); } void rkisp1_capture_isr(struct rkisp1_device *rkisp1) -- cgit v1.2.3 From 23780e14fec91242bb621670c61afa0898407892 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:31 +0200 Subject: media: staging: rkisp1: cap: move code that manages the buffers to rkisp1_set_next_buf The function 'rkisp1_set_next_buf' configures the registers according to 'cap->buf.next'. It is called after updating 'cap->buf.next' and 'cap->buf.curr'. This patch moves the code that updates those fields to rkisp1_set_next_buf. This is a preparation for later patch that change a call to 'rkisp1_handle_buffer' with a call to 'rkisp1_set_next_buf'. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index fa3eaeac2a00..7f400aefe550 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -575,12 +575,16 @@ static void rkisp1_dummy_buf_destroy(struct rkisp1_capture *cap) static void rkisp1_set_next_buf(struct rkisp1_capture *cap) { - /* - * Use the dummy space allocated by dma_alloc_coherent to - * throw data if there is no available buffer. - */ - if (cap->buf.next) { - u32 *buff_addr = cap->buf.next->buff_addr; + cap->buf.curr = cap->buf.next; + cap->buf.next = NULL; + + if (!list_empty(&cap->buf.queue)) { + u32 *buff_addr; + + cap->buf.next = list_first_entry(&cap->buf.queue, struct rkisp1_buffer, queue); + list_del(&cap->buf.next->queue); + + buff_addr = cap->buf.next->buff_addr; rkisp1_write(cap->rkisp1, buff_addr[RKISP1_PLANE_Y], @@ -592,6 +596,10 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) buff_addr[RKISP1_PLANE_CR], cap->config->mi.cr_base_ad_init); } else { + /* + * Use the dummy space allocated by dma_alloc_coherent to + * throw data if there is no available buffer. + */ rkisp1_write(cap->rkisp1, cap->buf.dummy.dma_addr, cap->config->mi.y_base_ad_init); @@ -632,16 +640,6 @@ static void rkisp1_handle_buffer(struct rkisp1_capture *cap) cap->rkisp1->debug.frame_drop[cap->id]++; } - cap->buf.curr = cap->buf.next; - cap->buf.next = NULL; - - if (!list_empty(&cap->buf.queue)) { - cap->buf.next = list_first_entry(&cap->buf.queue, - struct rkisp1_buffer, - queue); - list_del(&cap->buf.next->queue); - } - rkisp1_set_next_buf(cap); spin_unlock_irqrestore(&cap->buf.lock, flags); } -- cgit v1.2.3 From 20698ed90f018607e7a24c5258b7ed2dffafa7ae Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:32 +0200 Subject: media: staging: rkisp1: cap: in stream start, replace calls to rkisp1_handle_buffer with rkisp1_set_next_buf The function 'rkisp1_stream_start' calls 'rkisp1_handle_buffer' in order to update the 'buf.curr' and 'buf.next' fields and configure the device before streaming starts. This cause a wrong increment of the debugs field 'frame_drop'. This patch replaces the call to 'rkisp1_handle_buffer' with a call to 'rkisp1_set_next_buf'. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 7f400aefe550..c05280950ea0 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -916,7 +916,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) cap->ops->config(cap); /* Setup a buffer for the next frame */ - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); cap->ops->enable(cap); /* It's safe to config ACTIVE and SHADOW regs for the * first stream. While when the second is starting, do NOT @@ -931,7 +931,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) /* force cfg update */ rkisp1_write(rkisp1, RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); } cap->is_streaming = true; } -- cgit v1.2.3 From 8f611cc154965afdc1d8104e1d7b1b3df4bee7d2 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:51 +0200 Subject: media: staging: rkisp1: remove unused field ctrl_handler from struct rkisp1_device The field ctrl_handler in struct rkisp1_device is not used. This patch removes it. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 2603a5aa5210..3f0621814696 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -255,7 +255,6 @@ struct rkisp1_device { unsigned int clk_size; struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK]; struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; struct media_device media_dev; struct v4l2_async_notifier notifier; struct rkisp1_sensor_async *active_sensor; -- cgit v1.2.3 From 1b265b4d2c216ec3780125eebacb1035c7a5ed7a Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:52 +0200 Subject: media: staging: rkisp1: remove unused field alloc_ctx from struct rkisp1_device The field alloc_ctx in struct rkisp1_device is not used. This patch removes it. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 3f0621814696..2af8ffa0de52 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -264,7 +264,6 @@ struct rkisp1_device { struct rkisp1_stats stats; struct rkisp1_params params; struct media_pipeline pipe; - struct vb2_alloc_ctx *alloc_ctx; struct mutex stream_lock; struct rkisp1_debug debug; }; -- cgit v1.2.3 From 8d4a696af361af57bb84f3cc063473d241afc638 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:53 +0200 Subject: media: staging: rkisp1: set pads array of the resizer to size 2 Currently the 'pads' and 'pad_cfg' arrays of the rkisp1_resizer are of size 'RKISP1_ISP_PAD_MAX' which is 4. But the resizer has only two pads. Change the size of the arrays to 2 by adding and using 'RKISP1_RSZ_PAD_MAX' similar to the way it is done in the isp entity. [hverkuil: fix typos in commit log] Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 5 +++-- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 2af8ffa0de52..3dc51d703f73 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -52,6 +52,7 @@ enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, RKISP1_RSZ_PAD_SRC, + RKISP1_RSZ_PAD_MAX }; enum rkisp1_stream_id { @@ -219,8 +220,8 @@ struct rkisp1_resizer { struct v4l2_subdev sd; enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; - struct media_pad pads[RKISP1_ISP_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; + struct media_pad pads[RKISP1_RSZ_PAD_MAX]; + struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX]; const struct rkisp1_rsz_config *config; enum v4l2_pixel_encoding pixel_enc; struct mutex ops_lock; diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 31aea7dfc976..c66d2a52fd71 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -735,7 +735,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC; mutex_init(&rsz->ops_lock); - ret = media_entity_pads_init(&sd->entity, 2, pads); + ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) return ret; -- cgit v1.2.3 From 9bde425e70b6d6b49f3206a1d3817bccb92a893d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 22 Jul 2020 14:38:48 +0200 Subject: media: allegro: fix an error pointer vs NULL check The allegro_mbox_init() function returns error pointers, it never returns NULL. Fixes: 94dc76560261 ("media: allegro: rework mbox handling") Signed-off-by: Dan Carpenter Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 61beae1fca36..9f718f43282b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -2952,7 +2952,7 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, info->mailbox_size); dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, info->mailbox_size); - if (!dev->mbox_command || !dev->mbox_status) { + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { v4l2_err(&dev->v4l2_dev, "failed to initialize mailboxes\n"); return -EIO; -- cgit v1.2.3 From 04320b3ef90bef04df6672e3a98a266b88c35b55 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Jul 2020 15:09:03 +0200 Subject: media: allegro: fix potential null dereference on header The pointer header is an alias to msg and msg is being null checked. However, if msg is null then header is also null and this can lead to a null pointer dereference on the assignment type = header->type. Fix this just using header->type after the null check and removing the need for type as it is only used once. Addresses-Coverity: ("Dereference before null check") Fixes: 3de16839669f ("media: allegro: add explicit mail encoding and decoding") Signed-off-by: Colin Ian King Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-mail.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index 4ac65de12463..9286d2162377 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -462,13 +462,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) ssize_t allegro_encode_mail(u32 *dst, void *msg) { const struct mcu_msg_header *header = msg; - enum mcu_msg_type type = header->type; ssize_t size; if (!msg || !dst) return -EINVAL; - switch (type) { + switch (header->type) { case MCU_MSG_TYPE_INIT: size = allegro_enc_init(&dst[1], msg); break; -- cgit v1.2.3