aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-06 11:15:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-06 11:15:19 -0700
commit0b49ce5a40702bf78a5f80076312b244785e9a2f (patch)
tree1862fa8a30c3efbb539470af5fad1ee2fa8fe2e0 /drivers/staging
parentMerge tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound (diff)
parent[media] media: entity: Catch unbalanced media_pipeline_stop calls (diff)
downloadlinux-dev-0b49ce5a40702bf78a5f80076312b244785e9a2f.tar.xz
linux-dev-0b49ce5a40702bf78a5f80076312b244785e9a2f.zip
Merge tag 'media/v4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - addition of fwnode support at V4L2 core - addition of a few more SDR formats - new imx driver to support i.MX6 cameras - new driver for Qualcon venus codecs - new I2C sensor drivers: dw9714, max2175, ov13858, ov5640 - new CEC driver: stm32-cec - some improvements to DVB frontend documentation and a few fixups - several driver improvements and fixups * tag 'media/v4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (361 commits) [media] media: entity: Catch unbalanced media_pipeline_stop calls [media] media/uapi/v4l: clarify cropcap/crop/selection behavior [media] v4l2-ioctl/exynos: fix G/S_SELECTION's type handling [media] vimc: sen: Declare vimc_sen_video_ops as static [media] vimc: sca: Add scaler [media] vimc: deb: Add debayer filter [media] vimc: Subdevices as modules [media] vimc: cap: Support several image formats [media] vimc: sen: Support several image formats [media] vimc: common: Add vimc_colorimetry_clamp [media] vimc: common: Add vimc_link_validate [media] vimc: common: Add vimc_pipeline_s_stream helper [media] vimc: common: Add vimc_ent_sd_* helper [media] vimc: Move common code from the core [media] vimc: sen: Integrate the tpg on the sensor [media] media: i2c: ov772x: Force use of SCCB protocol [media] dvb uapi docs: enums are passed by value, not reference [media] dvb: don't use 'time_t' in event ioctl [media] media: venus: enable building with COMPILE_TEST [media] af9013: refactor power control ...
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/atomisp/i2c/Makefile6
-rw-r--r--drivers/staging/media/atomisp/i2c/gc0310.c1
-rw-r--r--drivers/staging/media/atomisp/i2c/imx/Makefile7
-rw-r--r--drivers/staging/media/atomisp/i2c/lm3554.c4
-rw-r--r--drivers/staging/media/atomisp/i2c/mt9m114.c2
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2680.c15
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/Makefile7
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/ov5693.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/Makefile7
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c14
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h23
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c36
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c13
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c10
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c297
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c34
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h7
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c8
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c4
-rw-r--r--drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c227
-rw-r--r--drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c12
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c6
-rw-r--r--drivers/staging/media/imx/Kconfig21
-rw-r--r--drivers/staging/media/imx/Makefile12
-rw-r--r--drivers/staging/media/imx/TODO23
-rw-r--r--drivers/staging/media/imx/imx-ic-common.c113
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c518
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c1309
-rw-r--r--drivers/staging/media/imx/imx-ic.h38
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c775
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c1817
-rw-r--r--drivers/staging/media/imx/imx-media-dev.c667
-rw-r--r--drivers/staging/media/imx/imx-media-fim.c494
-rw-r--r--drivers/staging/media/imx/imx-media-internal-sd.c349
-rw-r--r--drivers/staging/media/imx/imx-media-of.c270
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c896
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c1009
-rw-r--r--drivers/staging/media/imx/imx-media.h325
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c698
-rw-r--r--drivers/staging/media/lirc/TODO47
-rw-r--r--drivers/staging/media/lirc/TODO.lirc_zilog36
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c136
62 files changed, 9676 insertions, 728 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index dbda4d9a08e7..f8c25ee082ef 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -27,6 +27,8 @@ source "drivers/staging/media/cxd2099/Kconfig"
source "drivers/staging/media/davinci_vpfe/Kconfig"
+source "drivers/staging/media/imx/Kconfig"
+
source "drivers/staging/media/omap4iss/Kconfig"
# Keep LIRC at the end, as it has sub-menus
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index c04600c81264..ac090c5fce30 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_I2C_BCM2048) += bcm2048/
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile
index 466517c7c8e6..be13fab92175 100644
--- a/drivers/staging/media/atomisp/i2c/Makefile
+++ b/drivers/staging/media/atomisp/i2c/Makefile
@@ -19,3 +19,9 @@ obj-$(CONFIG_VIDEO_AP1302) += ap1302.o
obj-$(CONFIG_VIDEO_LM3554) += lm3554.o
+# HACK! While this driver is in bad shape, don't enable several warnings
+# that would be otherwise enabled with W=1
+ccflags-y += $(call cc-disable-warning, unused-but-set-variable)
+ccflags-y += $(call cc-disable-warning, unused-const-variable)
+ccflags-y += $(call cc-disable-warning, missing-prototypes)
+ccflags-y += $(call cc-disable-warning, missing-declarations)
diff --git a/drivers/staging/media/atomisp/i2c/gc0310.c b/drivers/staging/media/atomisp/i2c/gc0310.c
index 1ec616a15086..350fd7fd5b86 100644
--- a/drivers/staging/media/atomisp/i2c/gc0310.c
+++ b/drivers/staging/media/atomisp/i2c/gc0310.c
@@ -1455,6 +1455,7 @@ out_free:
static struct acpi_device_id gc0310_acpi_match[] = {
{"XXGC0310"},
+ {"INT0310"},
{},
};
diff --git a/drivers/staging/media/atomisp/i2c/imx/Makefile b/drivers/staging/media/atomisp/i2c/imx/Makefile
index 6b13a3a66e49..b6578f09546e 100644
--- a/drivers/staging/media/atomisp/i2c/imx/Makefile
+++ b/drivers/staging/media/atomisp/i2c/imx/Makefile
@@ -4,3 +4,10 @@ imx1x5-objs := imx.o drv201.o ad5816g.o dw9714.o dw9719.o dw9718.o vcm.o otp.o o
ov8858_driver-objs := ../ov8858.o dw9718.o vcm.o
obj-$(CONFIG_VIDEO_OV8858) += ov8858_driver.o
+
+# HACK! While this driver is in bad shape, don't enable several warnings
+# that would be otherwise enabled with W=1
+ccflags-y += $(call cc-disable-warning, unused-but-set-variable)
+ccflags-y += $(call cc-disable-warning, unused-const-variable)
+ccflags-y += $(call cc-disable-warning, missing-prototypes)
+ccflags-y += $(call cc-disable-warning, missing-declarations)
diff --git a/drivers/staging/media/atomisp/i2c/lm3554.c b/drivers/staging/media/atomisp/i2c/lm3554.c
index dd9c9c3ffff7..2b170c07aaba 100644
--- a/drivers/staging/media/atomisp/i2c/lm3554.c
+++ b/drivers/staging/media/atomisp/i2c/lm3554.c
@@ -497,7 +497,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.g_volatile_ctrl = lm3554_g_volatile_ctrl
};
-struct v4l2_ctrl_config lm3554_controls[] = {
+static const struct v4l2_ctrl_config lm3554_controls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_FLASH_TIMEOUT,
@@ -825,7 +825,7 @@ static int lm3554_gpio_uninit(struct i2c_client *client)
return 0;
}
-void *lm3554_platform_data_func(struct i2c_client *client)
+static void *lm3554_platform_data_func(struct i2c_client *client)
{
static struct lm3554_platform_data platform_data;
diff --git a/drivers/staging/media/atomisp/i2c/mt9m114.c b/drivers/staging/media/atomisp/i2c/mt9m114.c
index ced175c268d1..3fa915313e53 100644
--- a/drivers/staging/media/atomisp/i2c/mt9m114.c
+++ b/drivers/staging/media/atomisp/i2c/mt9m114.c
@@ -1499,7 +1499,7 @@ static struct v4l2_ctrl_config mt9m114_controls[] = {
.type = V4L2_CTRL_TYPE_MENU,
.min = 0,
.max = 3,
- .step = 1,
+ .step = 0,
.def = 1,
.flags = 0,
},
diff --git a/drivers/staging/media/atomisp/i2c/ov2680.c b/drivers/staging/media/atomisp/i2c/ov2680.c
index 566091035c64..3cabfe54c669 100644
--- a/drivers/staging/media/atomisp/i2c/ov2680.c
+++ b/drivers/staging/media/atomisp/i2c/ov2680.c
@@ -885,11 +885,12 @@ static int gpio_ctrl(struct v4l2_subdev *sd, bool flag)
if (flag) {
ret = dev->platform_data->gpio0_ctrl(sd, 1);
usleep_range(10000, 15000);
- ret |= dev->platform_data->gpio1_ctrl(sd, 1);
+ /* Ignore return from second gpio, it may not be there */
+ dev->platform_data->gpio1_ctrl(sd, 1);
usleep_range(10000, 15000);
} else {
- ret = dev->platform_data->gpio1_ctrl(sd, 0);
- ret |= dev->platform_data->gpio0_ctrl(sd, 0);
+ dev->platform_data->gpio1_ctrl(sd, 0);
+ ret = dev->platform_data->gpio0_ctrl(sd, 0);
}
return ret;
}
@@ -1190,9 +1191,8 @@ static int ov2680_detect(struct i2c_client *client)
OV2680_SC_CMMN_SUB_ID, &high);
revision = (u8) high & 0x0f;
- dev_err(&client->dev, "sensor_revision id = 0x%x\n", id);
- dev_err(&client->dev, "detect ov2680 success\n");
- dev_err(&client->dev, "################5##########\n");
+ dev_info(&client->dev, "sensor_revision id = 0x%x\n", id);
+
return 0;
}
@@ -1447,8 +1447,6 @@ static int ov2680_probe(struct i2c_client *client,
void *pdata;
unsigned int i;
- printk("++++ov2680_probe++++\n");
- dev_info(&client->dev, "++++ov2680_probe++++\n");
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_err(&client->dev, "out of memory\n");
@@ -1521,6 +1519,7 @@ out_free:
static struct acpi_device_id ov2680_acpi_match[] = {
{"XXOV2680"},
+ {"OVTI2680"},
{},
};
MODULE_DEVICE_TABLE(acpi, ov2680_acpi_match);
diff --git a/drivers/staging/media/atomisp/i2c/ov5693/Makefile b/drivers/staging/media/atomisp/i2c/ov5693/Makefile
index c9c0e1245858..4e3833aaec05 100644
--- a/drivers/staging/media/atomisp/i2c/ov5693/Makefile
+++ b/drivers/staging/media/atomisp/i2c/ov5693/Makefile
@@ -1 +1,8 @@
obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
+
+# HACK! While this driver is in bad shape, don't enable several warnings
+# that would be otherwise enabled with W=1
+ccflags-y += $(call cc-disable-warning, unused-but-set-variable)
+ccflags-y += $(call cc-disable-warning, unused-const-variable)
+ccflags-y += $(call cc-disable-warning, missing-prototypes)
+ccflags-y += $(call cc-disable-warning, missing-declarations)
diff --git a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c
index 5e9dafe7cc32..d6447398f5ef 100644
--- a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c
+++ b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.c
@@ -706,7 +706,7 @@ static int ov5693_read_otp_reg_array(struct i2c_client *client, u16 size,
{
u16 index;
int ret;
- u16 *pVal = 0;
+ u16 *pVal = NULL;
for (index = 0; index <= size; index++) {
pVal = (u16 *) (buf + index);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile
index f126a89a08e9..726eaa293c55 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile
+++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile
@@ -108,7 +108,6 @@ atomisp-objs += \
css2400/sh_css_metadata.o \
css2400/base/refcount/src/refcount.o \
css2400/base/circbuf/src/circbuf.o \
- css2400/sh_css_irq.o \
css2400/camera/pipe/src/pipe_binarydesc.o \
css2400/camera/pipe/src/pipe_util.o \
css2400/camera/pipe/src/pipe_stagedesc.o \
@@ -353,3 +352,9 @@ DEFINES += -DSYSTEM_hive_isp_css_2400_system -DISP2400
ccflags-y += $(INCLUDES) $(DEFINES) -fno-common
+# HACK! While this driver is in bad shape, don't enable several warnings
+# that would be otherwise enabled with W=1
+ccflags-y += -Wno-unused-const-variable -Wno-missing-prototypes \
+ -Wno-unused-but-set-variable -Wno-missing-declarations \
+ -Wno-suggest-attribute=format -Wno-missing-prototypes \
+ -Wno-implicit-fallthrough
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
index b830b241e2e6..ad2c610d2ce3 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
@@ -2506,7 +2506,6 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd,
struct ia_css_pipe_extra_config *pipe_extra_configs =
&stream_env->pipe_extra_configs[pipe_id];
unsigned int hor_ds_factor = 0, ver_ds_factor = 0;
-#define CEIL_DIV(a, b) ((b) ? ((a) + (b) - 1) / (b) : 0)
if (width == 0 && height == 0)
return;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
index 7ce8803cf6f9..c151c848cf8f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
@@ -130,9 +130,9 @@ static int atomisp_q_one_metadata_buffer(struct atomisp_sub_device *asd,
return 0;
}
-int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd,
- enum atomisp_input_stream_id stream_id,
- enum atomisp_css_pipe_id css_pipe_id)
+static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd,
+ enum atomisp_input_stream_id stream_id,
+ enum atomisp_css_pipe_id css_pipe_id)
{
struct atomisp_s3a_buf *s3a_buf;
struct list_head *s3a_list;
@@ -172,9 +172,9 @@ int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd,
return 0;
}
-int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd,
- enum atomisp_input_stream_id stream_id,
- enum atomisp_css_pipe_id css_pipe_id)
+static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd,
+ enum atomisp_input_stream_id stream_id,
+ enum atomisp_css_pipe_id css_pipe_id)
{
struct atomisp_dis_buf *dis_buf;
unsigned long irqflags;
@@ -744,7 +744,7 @@ static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd)
/*
* file operation functions
*/
-unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd)
+static unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd)
{
return asd->video_out_preview.users +
asd->video_out_vf.users +
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
index 6064bb823a47..aa0526ebaff1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
@@ -683,7 +683,7 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input)
int ret;
rt_mutex_lock(&isp->mutex);
- if (input >= ATOM_ISP_MAX_INPUTS || input > isp->input_cnt) {
+ if (input >= ATOM_ISP_MAX_INPUTS || input >= isp->input_cnt) {
dev_dbg(isp->dev, "input_cnt: %d\n", isp->input_cnt);
ret = -EINVAL;
goto error;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
index 996d1bdebad4..48b96048cab4 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
@@ -56,6 +56,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
+
if (format->pad)
return -EINVAL;
/* only raw8 grbg is supported by TPG */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
index e3fdbdba0b34..a543def739fc 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
@@ -51,12 +51,12 @@
/* G-Min addition: pull this in from intel_mid_pm.h */
#define CSTATE_EXIT_LATENCY_C1 1
-static uint skip_fwload = 0;
+static uint skip_fwload;
module_param(skip_fwload, uint, 0644);
MODULE_PARM_DESC(skip_fwload, "Skip atomisp firmware load");
/* set reserved memory pool size in page */
-unsigned int repool_pgnr;
+static unsigned int repool_pgnr;
module_param(repool_pgnr, uint, 0644);
MODULE_PARM_DESC(repool_pgnr,
"Set the reserved memory pool size in page (default:0)");
@@ -384,7 +384,7 @@ done:
* WA for DDR DVFS enable/disable
* By default, ISP will force DDR DVFS 1600MHz before disable DVFS
*/
-void punit_ddr_dvfs_enable(bool enable)
+static void punit_ddr_dvfs_enable(bool enable)
{
int reg = intel_mid_msgbus_read32(PUNIT_PORT, MRFLD_ISPSSDVFS);
int door_bell = 1 << 8;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile
index 04defaafa02c..ee5631b0e635 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/Makefile
@@ -1,4 +1,2 @@
ccflags-y += -DISP2400B0
ISP2400B0 := y
-
-include $(srctree)/$(src)/../Makefile.common
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h
index 48d84bc0ad9e..f74b405b0f39 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/math_support.h
@@ -62,15 +62,15 @@
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#ifdef ISP2401
-#define ROUND_DIV(a, b) ((b) ? ((a) + ((b) >> 1)) / (b) : 0)
+#define ROUND_DIV(a, b) (((b) != 0) ? ((a) + ((b) >> 1)) / (b) : 0)
#endif
-#define CEIL_DIV(a, b) ((b) ? ((a) + (b) - 1) / (b) : 0)
+#define CEIL_DIV(a, b) (((b) != 0) ? ((a) + (b) - 1) / (b) : 0)
#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b))
#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1))
#define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1)>>(b))
#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b))
#ifdef ISP2401
-#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0)
+#define ROUND_HALF_DOWN_DIV(a, b) (((b) != 0) ? ((a) + (b / 2) - 1) / (b) : 0)
#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b))
#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h
index 568631698a3d..c53241a7a281 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/string_support.h
@@ -72,9 +72,8 @@ static size_t strnlen_s(
return 0;
}
- for (ix=0;
- ((src_str[ix] != '\0') && (ix< max_len));
- ++ix) /*Nothing else to do*/;
+ for (ix = 0; ix < max_len && src_str[ix] != '\0'; ix++)
+ ;
/* On Error, it will return src_size == max_len*/
return ix;
@@ -118,7 +117,7 @@ STORAGE_CLASS_INLINE int strncpy_s(
/* dest_str is big enough for the len */
strncpy(dest_str, src_str, len);
- dest_str[len+1] = '\0';
+ dest_str[len] = '\0';
return 0;
}
@@ -158,7 +157,7 @@ STORAGE_CLASS_INLINE int strcpy_s(
/* dest_str is big enough for the len */
strncpy(dest_str, src_str, len);
- dest_str[len+1] = '\0';
+ dest_str[len] = '\0';
return 0;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h
index 7c8500903b5c..1021e4f380a5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mmu_private.h
@@ -1,4 +1,3 @@
-#ifdef ISP2401
/*
* Support for Intel Camera Imaging ISP subsystem.
* Copyright (c) 2015, Intel Corporation.
@@ -28,4 +27,3 @@ void
sh_css_mmu_set_page_table_base_index(hrt_data base_index);
#endif /* __IA_CSS_MMU_PRIVATE_H */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
index 0daab1176865..9478c12abe89 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
@@ -265,9 +265,9 @@ ia_css_translate_dvs_statistics(
assert(isp_stats->hor_proj != NULL);
assert(isp_stats->ver_proj != NULL);
- IA_CSS_ENTER("hproj=%p, vproj=%p, haddr=%x, vaddr=%x",
- host_stats->hor_proj, host_stats->ver_proj,
- isp_stats->hor_proj, isp_stats->ver_proj);
+ IA_CSS_ENTER("hproj=%p, vproj=%p, haddr=%p, vaddr=%p",
+ host_stats->hor_proj, host_stats->ver_proj,
+ isp_stats->hor_proj, isp_stats->ver_proj);
hor_num_isp = host_stats->grid.aligned_height;
ver_num_isp = host_stats->grid.aligned_width;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
index 5a0c103e9eb7..9bccb6473154 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
@@ -213,7 +213,7 @@ ia_css_translate_dvs2_statistics(
"hor_coefs.even_real=%p, hor_coefs.even_imag=%p, "
"ver_coefs.odd_real=%p, ver_coefs.odd_imag=%p, "
"ver_coefs.even_real=%p, ver_coefs.even_imag=%p, "
- "haddr=%x, vaddr=%x",
+ "haddr=%p, vaddr=%p",
host_stats->hor_prod.odd_real, host_stats->hor_prod.odd_imag,
host_stats->hor_prod.even_real, host_stats->hor_prod.even_imag,
host_stats->ver_prod.odd_real, host_stats->ver_prod.odd_imag,
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
index 804c19ab4485..222a7bd7f176 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
@@ -55,7 +55,7 @@ ia_css_tnr_dump(
"tnr_coef", tnr->coef);
ia_css_debug_dtrace(level, "\t%-32s = %d\n",
"tnr_threshold_Y", tnr->threshold_Y);
- ia_css_debug_dtrace(level, "\t%-32s = %d\n"
+ ia_css_debug_dtrace(level, "\t%-32s = %d\n",
"tnr_threshold_C", tnr->threshold_C);
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h
index 005eaaa9eb6c..2f215dc2ac32 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_const.h
@@ -398,17 +398,6 @@ more details.
* so the calc for the output buffer vmem size is:
* ((width[vectors]/num_of_stripes) + 2[vectors])
*/
-#if defined(HAS_RES_MGR)
-#define MAX_VECTORS_PER_OUTPUT_LINE \
- (CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS) + \
- ITERATOR_VECTOR_INCREMENT)
-
-#define MAX_VECTORS_PER_INPUT_LINE CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS)
-#define MAX_VECTORS_PER_INPUT_STRIPE (CEIL_ROUND_DIV_STRIPE(CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) , \
- ISP_NUM_STRIPES, \
- ISP_LEFT_PADDING_VECS) + \
- ITERATOR_VECTOR_INCREMENT)
-#else /* !defined(HAS_RES_MGR)*/
#define MAX_VECTORS_PER_OUTPUT_LINE \
CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS)
@@ -417,7 +406,6 @@ more details.
#define MAX_VECTORS_PER_INPUT_STRIPE CEIL_ROUND_DIV_STRIPE(MAX_VECTORS_PER_INPUT_LINE, \
ISP_NUM_STRIPES, \
ISP_LEFT_PADDING_VECS)
-#endif /* HAS_RES_MGR */
/* Add 2 for left croppping */
@@ -470,15 +458,11 @@ more details.
#define RAW_BUF_LINES ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2)
-#if defined(HAS_RES_MGR)
-#define RAW_BUF_STRIDE (MAX_VECTORS_PER_INPUT_STRIPE)
-#else /* !defined(HAS_RES_MGR) */
#define RAW_BUF_STRIDE \
(BINARY_ID == SH_CSS_BINARY_ID_POST_ISP ? MAX_VECTORS_PER_INPUT_CHUNK : \
ISP_NUM_STRIPES > 1 ? MAX_VECTORS_PER_INPUT_STRIPE+_ISP_EXTRA_PADDING_VECS : \
!ENABLE_CONTINUOUS ? MAX_VECTORS_PER_INPUT_LINE : \
MAX_VECTORS_PER_INPUT_CHUNK)
-#endif /* HAS_RES_MGR */
/* [isp vmem] table size[vectors] per line per color (GR,R,B,GB),
multiples of NWAY */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h
index 8b59a8caec52..e625ba62cc15 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h
@@ -214,24 +214,6 @@ more details.
/******* STRIPING-RELATED MACROS *******/
#define NO_STRIPING (ISP_NUM_STRIPES == 1)
-#if defined(HAS_RES_MGR)
-
-#define ISP_OUTPUT_CHUNK_VECS ISP_INTERNAL_WIDTH_VECS
-
-#if defined(__ISP)
-#define VECTORS_PER_LINE ISP_INTERNAL_WIDTH_VECS
-#else
-#define VECTORS_PER_LINE \
- (NO_STRIPING ? ISP_INTERNAL_WIDTH_VECS \
- : ISP_IO_STRIPE_WIDTH_VECS(ISP_INTERNAL_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) )
-#endif
-
-#define VECTORS_PER_INPUT_LINE \
- (NO_STRIPING ? ISP_INPUT_WIDTH_VECS \
- : ISP_IO_STRIPE_WIDTH_VECS(ISP_INPUT_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) )
-
-#else
-
#define ISP_OUTPUT_CHUNK_VECS \
(NO_STRIPING ? CEIL_DIV_CHUNKS(ISP_OUTPUT_VECS_EXTRA_CROP, OUTPUT_NUM_CHUNKS) \
: ISP_IO_STRIPE_WIDTH_VECS(ISP_OUTPUT_VECS_EXTRA_CROP, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) )
@@ -244,7 +226,6 @@ more details.
(NO_STRIPING ? ISP_INPUT_WIDTH_VECS \
: ISP_IO_STRIPE_WIDTH_VECS(ISP_INPUT_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH)+_ISP_EXTRA_PADDING_VECS)
-#endif
#define ISP_MAX_VF_OUTPUT_STRIPE_VECS \
(NO_STRIPING ? ISP_MAX_VF_OUTPUT_VECS \
@@ -282,11 +263,7 @@ more details.
#define OUTPUT_VECTORS_PER_CHUNK CEIL_DIV_CHUNKS(VECTORS_PER_LINE,OUTPUT_NUM_CHUNKS)
/* should be even?? */
-#if !defined(HAS_RES_MGR)
#define OUTPUT_C_VECTORS_PER_CHUNK CEIL_DIV(OUTPUT_VECTORS_PER_CHUNK, 2)
-#else
-#define OUTPUT_C_VECTORS_PER_CHUNK CEIL_DIV(MAX_VECTORS_PER_CHUNK, 2)
-#endif
#ifndef ISP2401
/**** SCTBL defs *******/
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
index a8b93a756e41..9f8a125f0d74 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
@@ -36,10 +36,6 @@
#endif
#include "camera/pipe/interface/ia_css_pipe_binarydesc.h"
-#if defined(HAS_RES_MGR)
-#include <components/resolutions_mgr/src/host/resolutions_mgr.host.h>
-#include <components/acc_cluster/acc_dvs_stat/host/dvs_stat.host.h>
-#endif
#include "memory_access.h"
@@ -110,10 +106,6 @@ ia_css_binary_internal_res(const struct ia_css_frame_info *in_info,
internal_res->height = __ISP_INTERNAL_HEIGHT(isp_tmp_internal_height,
info->pipeline.top_cropping,
binary_dvs_env.height);
-#if defined(HAS_RES_MGR)
- internal_res->height = (bds_out_info == NULL) ? internal_res->height : bds_out_info->res.height;
- internal_res->width = (bds_out_info == NULL) ? internal_res->width: bds_out_info->res.width;
-#endif
}
#ifndef ISP2401
@@ -787,25 +779,6 @@ ia_css_binary_dvs_stat_grid_info(
struct ia_css_grid_info *info,
struct ia_css_pipe *pipe)
{
-#if defined(HAS_RES_MGR)
- struct ia_css_dvs_stat_grid_info *dvs_stat_info;
- unsigned int i;
-
- assert(binary != NULL);
- assert(info != NULL);
- dvs_stat_info = &info->dvs_grid.dvs_stat_grid_info;
-
- if (binary->info->sp.enable.dvs_stats) {
- for (i = 0; i < IA_CSS_SKC_DVS_STAT_NUM_OF_LEVELS; i++) {
- dvs_stat_info->grd_cfg[i].grd_start.enable = 1;
- }
- ia_css_dvs_stat_grid_calculate(pipe, dvs_stat_info);
- }
- else {
- memset(dvs_stat_info, 0, sizeof(struct ia_css_dvs_stat_grid_info));
- }
-
-#endif
(void)pipe;
sh_css_binary_common_grid_info(binary, info);
return;
@@ -1088,9 +1061,6 @@ binary_in_frame_padded_width(int in_frame_width,
/* in other cases, the left padding pixels are always 128 */
nr_of_left_paddings = 2*ISP_VEC_NELEMS;
#endif
-#if defined(HAS_RES_MGR)
- (void)dvs_env_width;
-#endif
if (need_scaling) {
/* In SDV use-case, we need to match left-padding of
* primary and the video binary. */
@@ -1101,9 +1071,7 @@ binary_in_frame_padded_width(int in_frame_width,
2*ISP_VEC_NELEMS);
} else {
/* Different than before, we do left&right padding. */
-#if !defined(HAS_RES_MGR) /* dvs env is included already */
in_frame_width += dvs_env_width;
-#endif
rval =
CEIL_MUL(in_frame_width +
(left_cropping ? nr_of_left_paddings : 0),
@@ -1214,10 +1182,8 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->in_frame_info.res.width = in_info->res.width + info->pipeline.left_cropping;
binary->in_frame_info.res.height = in_info->res.height + info->pipeline.top_cropping;
-#if !defined(HAS_RES_MGR) /* dvs env is included already */
binary->in_frame_info.res.width += dvs_env_width;
binary->in_frame_info.res.height += dvs_env_height;
-#endif
binary->in_frame_info.padded_width =
binary_in_frame_padded_width(in_info->res.width,
@@ -1658,7 +1624,7 @@ ia_css_binary_find(struct ia_css_binary_descr *descr,
candidate->internal.max_height);
continue;
}
- if (!candidate->enable.ds && need_ds & !(xcandidate->num_output_pins > 1)) {
+ if (!candidate->enable.ds && need_ds && !(xcandidate->num_output_pins > 1)) {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
"ia_css_binary_find() [%d] continue: !%d && %d\n",
__LINE__, candidate->enable.ds, (int)need_ds);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
index ed33d4c4c84a..5d40afd482f5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
@@ -239,7 +239,7 @@ static ia_css_queue_t *bufq_get_qhandle(
enum sh_css_queue_id id,
int thread)
{
- ia_css_queue_t *q = 0;
+ ia_css_queue_t *q = NULL;
switch (type) {
case sh_css_host2sp_buffer_queue:
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h
index be7df3a30c21..91c105cc6204 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/interface/ia_css_debug.h
@@ -137,6 +137,7 @@ ia_css_debug_vdtrace(unsigned int level, const char *fmt, va_list args)
sh_css_vprint(fmt, args);
}
+__printf(2, 3)
extern void ia_css_debug_dtrace(unsigned int level, const char *fmt, ...);
/*! @brief Dump sp thread's stack contents
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
index 030810bd0878..0fa7cb2423d8 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
@@ -176,7 +176,6 @@ void ia_css_debug_dtrace(unsigned int level, const char *fmt, ...)
va_end(ap);
}
-#if !defined(HRT_UNSCHED)
static void debug_dump_long_array_formatted(
const sp_ID_t sp_id,
hrt_address stack_sp_addr,
@@ -249,12 +248,6 @@ void ia_css_debug_dump_sp_stack_info(void)
{
debug_dump_sp_stack_info(SP0_ID);
}
-#else
-/* Empty def for crun */
-void ia_css_debug_dump_sp_stack_info(void)
-{
-}
-#endif /* #if !HRT_UNSCHED */
void ia_css_debug_set_dtrace_level(const unsigned int trace_level)
@@ -3148,8 +3141,8 @@ ia_css_debug_dump_pipe_config(
ia_css_debug_dump_frame_info(&config->vf_output_info[i],
"vf_output_info");
}
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: 0x%x\n",
- config->acc_extension);
+ ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: %p\n",
+ config->acc_extension);
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "num_acc_stages: %d\n",
config->num_acc_stages);
ia_css_debug_dump_capture_config(&config->default_capture_config);
@@ -3179,7 +3172,7 @@ ia_css_debug_dump_stream_config_source(
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "timeout: %d\n",
config->source.port.timeout);
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "compression: %d\n",
- config->source.port.compression);
+ config->source.port.compression.type);
break;
case IA_CSS_INPUT_MODE_TPG:
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "source.tpg\n");
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c
index b36d7b00ebe8..d9178e80dab2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/spctrl/src/spctrl.c
@@ -57,17 +57,11 @@ enum ia_css_err ia_css_spctrl_load_fw(sp_ID_t sp_id,
hrt_vaddress code_addr = mmgr_NULL;
struct ia_css_sp_init_dmem_cfg *init_dmem_cfg;
- if ((sp_id >= N_SP_ID) || (spctrl_cfg == 0))
+ if ((sp_id >= N_SP_ID) || (spctrl_cfg == NULL))
return IA_CSS_ERR_INVALID_ARGUMENTS;
spctrl_cofig_info[sp_id].code_addr = mmgr_NULL;
-#if defined(HRT_UNSCHED)
- (void)init_dmem_cfg;
- code_addr = mmgr_malloc(1);
- if (code_addr == mmgr_NULL)
- return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
-#else
init_dmem_cfg = &spctrl_cofig_info[sp_id].dmem_config;
init_dmem_cfg->dmem_data_addr = spctrl_cfg->dmem_data_addr;
init_dmem_cfg->dmem_bss_addr = spctrl_cfg->dmem_bss_addr;
@@ -104,7 +98,7 @@ enum ia_css_err ia_css_spctrl_load_fw(sp_ID_t sp_id,
code_addr = mmgr_NULL;
return IA_CSS_ERR_INTERNAL_ERROR;
}
-#endif
+
spctrl_cofig_info[sp_id].sp_entry = spctrl_cfg->sp_entry;
spctrl_cofig_info[sp_id].code_addr = code_addr;
spctrl_cofig_info[sp_id].program_name = spctrl_cfg->program_name;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
index 73c76583610a..471f2be974e2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
@@ -64,7 +64,7 @@
#include "input_system.h"
#endif
#include "mmu_device.h" /* mmu_set_page_table_base_index(), ... */
-//#include "ia_css_mmu_private.h" /* sh_css_mmu_set_page_table_base_index() */
+#include "ia_css_mmu_private.h" /* sh_css_mmu_set_page_table_base_index() */
#include "gdc_device.h" /* HRT_GDC_N */
#include "dma.h" /* dma_set_max_burst_size() */
#include "irq.h" /* virq */
@@ -98,18 +98,8 @@ static int thread_alive;
#include "isp/modes/interface/input_buf.isp.h"
-#if defined(HAS_BL)
-#include "support/bootloader/interface/ia_css_blctrl.h"
-#endif
-#if defined(HAS_RES_MGR)
-#include "components/acc_cluster/gen/host/acc_cluster.host.h"
-#endif
-
/* Name of the sp program: should not be built-in */
#define SP_PROG_NAME "sp"
-#if defined(HAS_BL)
-#define BL_PROG_NAME "bootloader"
-#endif
/* Size of Refcount List */
#define REFCOUNT_SIZE 1000
@@ -252,11 +242,6 @@ ia_css_reset_defaults(struct sh_css* css);
static void
sh_css_init_host_sp_control_vars(void);
-#ifndef ISP2401
-static void
-sh_css_mmu_set_page_table_base_index(hrt_data base_index);
-
-#endif
static enum ia_css_err set_num_primary_stages(unsigned int *num, enum ia_css_pipe_version version);
static bool
@@ -385,13 +370,8 @@ sh_css_hmm_buffer_record_uninit(void);
static void
sh_css_hmm_buffer_record_reset(struct sh_css_hmm_buffer_record *buffer_record);
-#ifndef ISP2401
-static bool
-sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf,
-#else
static struct sh_css_hmm_buffer_record
*sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf,
-#endif
enum ia_css_buffer_type type,
hrt_address kernel_ptr);
@@ -1475,30 +1455,17 @@ static void start_pipe(
copy_ovrd,
input_mode,
&me->stream->config.metadata_config,
-#ifndef ISP2401
&me->stream->info.metadata_info
-#else
- &me->stream->info.metadata_info,
-#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
-#ifndef ISP2401
- , (input_mode==IA_CSS_INPUT_MODE_MEMORY)?
-#else
- (input_mode == IA_CSS_INPUT_MODE_MEMORY) ?
-#endif
+ ,(input_mode==IA_CSS_INPUT_MODE_MEMORY) ?
(mipi_port_ID_t)0 :
-#ifndef ISP2401
me->stream->config.source.port.port
-#else
- me->stream->config.source.port.port,
#endif
+#ifdef ISP2401
+ ,&me->config.internal_frame_origin_bqs_on_sctbl,
+ me->stream->isp_params_configs
#endif
-#ifndef ISP2401
- );
-#else
- &me->config.internal_frame_origin_bqs_on_sctbl,
- me->stream->isp_params_configs);
-#endif
+ );
if (me->config.mode != IA_CSS_PIPE_MODE_COPY) {
struct ia_css_pipeline_stage *stage;
@@ -1571,34 +1538,7 @@ enable_interrupts(enum ia_css_irq_type irq_type)
}
#endif
-#if defined(HAS_BL)
-static bool sh_css_setup_blctrl_config(const struct ia_css_fw_info *fw,
- const char *program,
- ia_css_blctrl_cfg *blctrl_cfg)
-{
- if((fw == NULL)||(blctrl_cfg == NULL))
- return false;
- blctrl_cfg->bl_entry = 0;
- blctrl_cfg->program_name = (char *)(program);
-
-#if !defined(HRT_UNSCHED)
- blctrl_cfg->ddr_data_offset = fw->blob.data_source;
- blctrl_cfg->dmem_data_addr = fw->blob.data_target;
- blctrl_cfg->dmem_bss_addr = fw->blob.bss_target;
- blctrl_cfg->data_size = fw->blob.data_size ;
- blctrl_cfg->bss_size = fw->blob.bss_size;
- blctrl_cfg->blctrl_state_dmem_addr = fw->info.bl.sw_state;
- blctrl_cfg->blctrl_dma_cmd_list = fw->info.bl.dma_cmd_list;
- blctrl_cfg->blctrl_nr_of_dma_cmds = fw->info.bl.num_dma_cmds;
-
- blctrl_cfg->code_size = fw->blob.size;
- blctrl_cfg->code = fw->blob.code;
- blctrl_cfg->bl_entry = fw->info.bl.bl_entry; /* entry function ptr on Bootloader */
-#endif
- return true;
-}
-#endif
static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw,
const char * program,
ia_css_spctrl_cfg *spctrl_cfg)
@@ -1608,7 +1548,6 @@ static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw,
spctrl_cfg->sp_entry = 0;
spctrl_cfg->program_name = (char *)(program);
-#if !defined(HRT_UNSCHED)
spctrl_cfg->ddr_data_offset = fw->blob.data_source;
spctrl_cfg->dmem_data_addr = fw->blob.data_target;
spctrl_cfg->dmem_bss_addr = fw->blob.bss_target;
@@ -1621,7 +1560,7 @@ static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw,
spctrl_cfg->code_size = fw->blob.size;
spctrl_cfg->code = fw->blob.code;
spctrl_cfg->sp_entry = fw->info.sp.sp_entry; /* entry function ptr on SP */
-#endif
+
return true;
}
void
@@ -1708,9 +1647,6 @@ ia_css_init(const struct ia_css_env *env,
{
enum ia_css_err err;
ia_css_spctrl_cfg spctrl_cfg;
-#if defined(HAS_BL)
- ia_css_blctrl_cfg blctrl_cfg;
-#endif
void (*flush_func)(struct ia_css_acc_fw *fw);
hrt_data select, enable;
@@ -1863,26 +1799,6 @@ ia_css_init(const struct ia_css_env *env,
return err;
}
-#if defined(HAS_BL)
- if (!sh_css_setup_blctrl_config(&sh_css_bl_fw, BL_PROG_NAME, &blctrl_cfg))
- return IA_CSS_ERR_INTERNAL_ERROR;
- err = ia_css_blctrl_load_fw(&blctrl_cfg);
- if (err != IA_CSS_SUCCESS) {
- IA_CSS_LEAVE_ERR(err);
- return err;
- }
-
-#ifdef ISP2401
- err = ia_css_blctrl_add_target_fw_info(&sh_css_sp_fw, IA_CSS_SP0,
- get_sp_code_addr(SP0_ID));
-
-#endif
- if (err != IA_CSS_SUCCESS) {
- IA_CSS_LEAVE_ERR(err);
- return err;
- }
-#endif /* HAS_BL */
-
#if WITH_PC_MONITORING
if (!thread_alive) {
thread_alive++;
@@ -2003,7 +1919,7 @@ ia_css_enable_isys_event_queue(bool enable)
void *sh_css_malloc(size_t size)
{
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_malloc() enter: size=%d\n",size);
+ ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_malloc() enter: size=%zu\n",size);
/* FIXME: This first test can probably go away */
if (size == 0)
return NULL;
@@ -2016,7 +1932,7 @@ void *sh_css_calloc(size_t N, size_t size)
{
void *p;
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_calloc() enter: N=%d, size=%d\n",N,size);
+ ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_calloc() enter: N=%zu, size=%zu\n",N,size);
/* FIXME: this test can probably go away */
if (size > 0) {
@@ -2059,7 +1975,8 @@ map_sp_threads(struct ia_css_stream *stream, bool map)
enum ia_css_pipe_id pipe_id;
assert(stream != NULL);
- IA_CSS_ENTER_PRIVATE("stream = %p, map = %p", stream, map);
+ IA_CSS_ENTER_PRIVATE("stream = %p, map = %s",
+ stream, map ? "true" : "false");
if (stream == NULL) {
IA_CSS_LEAVE_ERR_PRIVATE(IA_CSS_ERR_INVALID_ARGUMENTS);
@@ -2611,15 +2528,8 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe)
break;
}
-#ifndef ISP2401
- if (pipe->scaler_pp_lut != mmgr_NULL) {
- hmm_free(pipe->scaler_pp_lut);
- pipe->scaler_pp_lut = mmgr_NULL;
- }
-#else
sh_css_params_free_gdc_lut(pipe->scaler_pp_lut);
pipe->scaler_pp_lut = mmgr_NULL;
-#endif
my_css.active_pipes[ia_css_pipe_get_pipe_num(pipe)] = NULL;
sh_css_pipe_free_shading_table(pipe);
@@ -2666,9 +2576,6 @@ ia_css_uninit(void)
}
ia_css_spctrl_unload_fw(SP0_ID);
sh_css_sp_set_sp_running(false);
-#if defined(HAS_BL)
- ia_css_blctrl_unload_fw();
-#endif
#if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401)
/* check and free any remaining mipi frames */
free_mipi_frames(NULL);
@@ -2683,23 +2590,6 @@ ia_css_uninit(void)
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_uninit() leave: return_void\n");
}
-#ifndef ISP2401
-/* Deprecated, this is an HRT backend function (memory_access.h) */
-static void
-sh_css_mmu_set_page_table_base_index(hrt_data base_index)
-{
- int i;
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "sh_css_mmu_set_page_table_base_index() enter: base_index=0x%08x\n",base_index);
- my_css.page_table_base_index = base_index;
- for (i = 0; i < (int)N_MMU_ID; i++) {
- mmu_ID_t mmu_id = (mmu_ID_t)i;
- mmu_set_page_table_base_index(mmu_id, base_index);
- mmu_invalidate_cache(mmu_id);
- }
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "sh_css_mmu_set_page_table_base_index() leave: return_void\n");
-}
-
-#endif
#if defined(HAS_IRQ_MAP_VERSION_2)
enum ia_css_err ia_css_irq_translate(
unsigned int *irq_infos)
@@ -2766,7 +2656,7 @@ enum ia_css_err ia_css_irq_translate(
*irq_infos = infos;
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_irq_translate() "
- "leave: irq_infos=%p\n", infos);
+ "leave: irq_infos=%u\n", infos);
return IA_CSS_SUCCESS;
}
@@ -3004,11 +2894,8 @@ load_preview_binaries(struct ia_css_pipe *pipe)
#endif
/* preview only have 1 output pin now */
struct ia_css_frame_info *pipe_out_info = &pipe->output_info[0];
-#ifdef ISP2401
struct ia_css_preview_settings *mycs = &pipe->pipe_settings.preview;
-#endif
-
IA_CSS_ENTER_PRIVATE("");
assert(pipe != NULL);
assert(pipe->stream != NULL);
@@ -3020,11 +2907,7 @@ load_preview_binaries(struct ia_css_pipe *pipe)
sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR;
#endif
-#ifndef ISP2401
- if (pipe->pipe_settings.preview.preview_binary.info)
-#else
if (mycs->preview_binary.info)
-#endif
return IA_CSS_SUCCESS;
err = ia_css_util_check_input(&pipe->stream->config, false, false);
@@ -3077,12 +2960,7 @@ load_preview_binaries(struct ia_css_pipe *pipe)
&prev_vf_info);
if (err != IA_CSS_SUCCESS)
return err;
- err = ia_css_binary_find(&preview_descr,
-#ifndef ISP2401
- &pipe->pipe_settings.preview.preview_binary);
-#else
- &mycs->preview_binary);
-#endif
+ err = ia_css_binary_find(&preview_descr, &mycs->preview_binary);
if (err != IA_CSS_SUCCESS)
return err;
@@ -3098,24 +2976,15 @@ load_preview_binaries(struct ia_css_pipe *pipe)
#endif
/* The vf_pp binary is needed when (further) YUV downscaling is required */
-#ifndef ISP2401
- need_vf_pp |= pipe->pipe_settings.preview.preview_binary.out_frame_info[0].res.width != pipe_out_info->res.width;
- need_vf_pp |= pipe->pipe_settings.preview.preview_binary.out_frame_info[0].res.height != pipe_out_info->res.height;
-#else
need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.width != pipe_out_info->res.width;
need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.height != pipe_out_info->res.height;
-#endif
/* When vf_pp is needed, then the output format of the selected
* preview binary must be yuv_line. If this is not the case,
* then the preview binary selection is done again.
*/
if (need_vf_pp &&
-#ifndef ISP2401
- (pipe->pipe_settings.preview.preview_binary.out_frame_info[0].format != IA_CSS_FRAME_FORMAT_YUV_LINE)) {
-#else
(mycs->preview_binary.out_frame_info[0].format != IA_CSS_FRAME_FORMAT_YUV_LINE)) {
-#endif
/* Preview step 2 */
if (pipe->vf_yuv_ds_input_info.res.width)
@@ -3136,11 +3005,7 @@ load_preview_binaries(struct ia_css_pipe *pipe)
if (err != IA_CSS_SUCCESS)
return err;
err = ia_css_binary_find(&preview_descr,
-#ifndef ISP2401
- &pipe->pipe_settings.preview.preview_binary);
-#else
&mycs->preview_binary);
-#endif
if (err != IA_CSS_SUCCESS)
return err;
}
@@ -3150,18 +3015,10 @@ load_preview_binaries(struct ia_css_pipe *pipe)
/* Viewfinder post-processing */
ia_css_pipe_get_vfpp_binarydesc(pipe, &vf_pp_descr,
-#ifndef ISP2401
- &pipe->pipe_settings.preview.preview_binary.out_frame_info[0],
-#else
&mycs->preview_binary.out_frame_info[0],
-#endif
pipe_out_info);
err = ia_css_binary_find(&vf_pp_descr,
-#ifndef ISP2401
- &pipe->pipe_settings.preview.vf_pp_binary);
-#else
&mycs->vf_pp_binary);
-#endif
if (err != IA_CSS_SUCCESS)
return err;
}
@@ -3187,13 +3044,8 @@ load_preview_binaries(struct ia_css_pipe *pipe)
/* Copy */
if (need_isp_copy_binary) {
err = load_copy_binary(pipe,
-#ifndef ISP2401
- &pipe->pipe_settings.preview.copy_binary,
- &pipe->pipe_settings.preview.preview_binary);
-#else
&mycs->copy_binary,
&mycs->preview_binary);
-#endif
if (err != IA_CSS_SUCCESS)
return err;
}
@@ -4499,22 +4351,10 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe,
}
if (return_err == IA_CSS_SUCCESS) {
-#ifndef ISP2401
- bool found_record = false;
- found_record = sh_css_hmm_buffer_record_acquire(
-#else
- struct sh_css_hmm_buffer_record *hmm_buffer_record = NULL;
-
- hmm_buffer_record = sh_css_hmm_buffer_record_acquire(
-#endif
- h_vbuf, buf_type,
- HOST_ADDRESS(ddr_buffer.kernel_ptr));
-#ifndef ISP2401
- if (found_record == true) {
-#else
- if (hmm_buffer_record) {
-#endif
- IA_CSS_LOG("send vbuf=0x%x", h_vbuf);
+ if (sh_css_hmm_buffer_record_acquire(
+ h_vbuf, buf_type,
+ HOST_ADDRESS(ddr_buffer.kernel_ptr))) {
+ IA_CSS_LOG("send vbuf=%p", h_vbuf);
} else {
return_err = IA_CSS_ERR_INTERNAL_ERROR;
IA_CSS_ERROR("hmm_buffer_record[]: no available slots\n");
@@ -4624,7 +4464,7 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
ia_css_rmgr_rel_vbuf(hmm_buffer_pool, &hmm_buffer_record->h_vbuf);
sh_css_hmm_buffer_record_reset(hmm_buffer_record);
} else {
- IA_CSS_ERROR("hmm_buffer_record not found (0x%p) buf_type(%d)",
+ IA_CSS_ERROR("hmm_buffer_record not found (0x%u) buf_type(%d)",
ddr_buffer_addr, buf_type);
IA_CSS_LEAVE_ERR(IA_CSS_ERR_INTERNAL_ERROR);
return IA_CSS_ERR_INTERNAL_ERROR;
@@ -4640,8 +4480,8 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
if ((ddr_buffer.kernel_ptr == 0) ||
(kernel_ptr != HOST_ADDRESS(ddr_buffer.kernel_ptr))) {
IA_CSS_ERROR("kernel_ptr invalid");
- IA_CSS_ERROR("expected: (0x%p)", kernel_ptr);
- IA_CSS_ERROR("actual: (0x%p)", HOST_ADDRESS(ddr_buffer.kernel_ptr));
+ IA_CSS_ERROR("expected: (0x%llx)", (u64)kernel_ptr);
+ IA_CSS_ERROR("actual: (0x%llx)", (u64)HOST_ADDRESS(ddr_buffer.kernel_ptr));
IA_CSS_ERROR("buf_type: %d\n", buf_type);
IA_CSS_LEAVE_ERR(IA_CSS_ERR_INTERNAL_ERROR);
return IA_CSS_ERR_INTERNAL_ERROR;
@@ -6316,9 +6156,6 @@ static enum ia_css_err load_primary_binaries(
#else
*pipe_vf_out_info;
#endif
-#if defined(HAS_RES_MGR)
- struct ia_css_frame_info bds_out_info;
-#endif
enum ia_css_err err = IA_CSS_SUCCESS;
struct ia_css_capture_settings *mycs;
unsigned int i;
@@ -6440,10 +6277,6 @@ static enum ia_css_err load_primary_binaries(
&cas_scaler_descr.out_info[i],
&cas_scaler_descr.internal_out_info[i],
&cas_scaler_descr.vf_info[i]);
-#if defined(HAS_RES_MGR)
- bds_out_info.res = pipe->config.bayer_ds_out_res;
- yuv_scaler_descr.bds_out_info = &bds_out_info;
-#endif
err = ia_css_binary_find(&yuv_scaler_descr,
&mycs->yuv_scaler_binary[i]);
if (err != IA_CSS_SUCCESS) {
@@ -6494,10 +6327,6 @@ static enum ia_css_err load_primary_binaries(
&capture_pp_descr, &prim_out_info,
#endif
&capt_pp_out_info, &vf_info);
-#if defined(HAS_RES_MGR)
- bds_out_info.res = pipe->config.bayer_ds_out_res;
- capture_pp_descr.bds_out_info = &bds_out_info;
-#endif
err = ia_css_binary_find(&capture_pp_descr,
&mycs->capture_pp_binary);
if (err != IA_CSS_SUCCESS) {
@@ -6533,10 +6362,6 @@ static enum ia_css_err load_primary_binaries(
if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1))
local_vf_info = &vf_info;
ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], &prim_in_info, &prim_out_info, local_vf_info, i);
-#if defined(HAS_RES_MGR)
- bds_out_info.res = pipe->config.bayer_ds_out_res;
- prim_descr[i].bds_out_info = &bds_out_info;
-#endif
err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]);
if (err != IA_CSS_SUCCESS) {
IA_CSS_LEAVE_ERR_PRIVATE(err);
@@ -6570,10 +6395,6 @@ static enum ia_css_err load_primary_binaries(
ia_css_pipe_get_vfpp_binarydesc(pipe,
&vf_pp_descr, vf_pp_in_info, pipe_vf_out_info);
-#if defined(HAS_RES_MGR)
- bds_out_info.res = pipe->config.bayer_ds_out_res;
- vf_pp_descr.bds_out_info = &bds_out_info;
-#endif
err = ia_css_binary_find(&vf_pp_descr, &mycs->vf_pp_binary);
if (err != IA_CSS_SUCCESS) {
IA_CSS_LEAVE_ERR_PRIVATE(err);
@@ -6621,7 +6442,7 @@ allocate_delay_frames(struct ia_css_pipe *pipe)
IA_CSS_ENTER_PRIVATE("");
if (pipe == NULL) {
- IA_CSS_ERROR("Invalid args - pipe %x", pipe);
+ IA_CSS_ERROR("Invalid args - pipe %p", pipe);
return IA_CSS_ERR_INVALID_ARGUMENTS;
}
@@ -8628,9 +8449,7 @@ remove_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware)
return; /* removing single and multiple firmware is handled in acc_unload_extension() */
}
-#if !defined(HRT_UNSCHED)
-static enum ia_css_err
-upload_isp_code(struct ia_css_fw_info *firmware)
+static enum ia_css_err upload_isp_code(struct ia_css_fw_info *firmware)
{
hrt_vaddress binary;
@@ -8658,12 +8477,10 @@ upload_isp_code(struct ia_css_fw_info *firmware)
return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
return IA_CSS_SUCCESS;
}
-#endif
static enum ia_css_err
acc_load_extension(struct ia_css_fw_info *firmware)
{
-#if !defined(HRT_UNSCHED)
enum ia_css_err err;
struct ia_css_fw_info *hd = firmware;
while (hd){
@@ -8672,7 +8489,6 @@ acc_load_extension(struct ia_css_fw_info *firmware)
return err;
hd = hd->next;
}
-#endif
if (firmware == NULL)
return IA_CSS_ERR_INVALID_ARGUMENTS;
@@ -9879,9 +9695,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
/* take over effective info */
effective_res = curr_pipe->config.input_effective_res;
-#endif
-
-#ifndef ISP2401
err = ia_css_util_check_res(
effective_res.width,
effective_res.height);
@@ -9902,13 +9715,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
if (err != IA_CSS_SUCCESS)
goto ERR;
-#if defined(HAS_RES_MGR)
- /* update acc configuration - striping info is ready */
- err = ia_css_update_cfg_stripe_info(curr_pipe);
- if (err != IA_CSS_SUCCESS)
- goto ERR;
-#endif
-
/* handle each pipe */
pipe_info = &curr_pipe->info;
for (j = 0; j < IA_CSS_PIPE_MAX_OUTPUT_STAGE; j++) {
@@ -10587,39 +10393,6 @@ ia_css_pipe_get_isp_pipe_version(const struct ia_css_pipe *pipe)
return (unsigned int)pipe->config.isp_pipe_version;
}
-#if defined(HAS_BL)
-#define BL_START_TIMEOUT_US 30000000
-static enum ia_css_err
-ia_css_start_bl(void)
-{
- enum ia_css_err err = IA_CSS_SUCCESS;
- unsigned long timeout;
-
- IA_CSS_ENTER("");
- sh_css_start_bl();
- /* waiting for the Bootloader to complete execution */
- timeout = BL_START_TIMEOUT_US;
- while((ia_css_blctrl_get_state() == BOOTLOADER_BUSY) && timeout) {
- timeout--;
- hrt_sleep();
- }
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "Bootloader state %d\n", ia_css_blctrl_get_state());
- if (timeout == 0) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
- "Bootloader Execution Timeout\n");
- err = IA_CSS_ERR_INTERNAL_ERROR;
- }
- if (ia_css_blctrl_get_state() != BOOTLOADER_OK) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
- "Bootloader Execution Failed\n");
- err = IA_CSS_ERR_INTERNAL_ERROR;
- }
- IA_CSS_LEAVE_ERR(err);
- return err;
-}
-#endif
-
#define SP_START_TIMEOUT_US 30000000
enum ia_css_err
@@ -10629,15 +10402,6 @@ ia_css_start_sp(void)
enum ia_css_err err = IA_CSS_SUCCESS;
IA_CSS_ENTER("");
-#if defined(HAS_BL)
- /* Starting bootloader before Sp0 and Sp1
- * and not exposing CSS API */
- err = ia_css_start_bl();
- if (err != IA_CSS_SUCCESS) {
- IA_CSS_LEAVE("Bootloader fails");
- return err;
- }
-#endif
sh_css_sp_start_isp();
/* waiting for the SP is completely started */
@@ -11291,23 +11055,14 @@ sh_css_hmm_buffer_record_reset(struct sh_css_hmm_buffer_record *buffer_record)
buffer_record->kernel_ptr = 0;
}
-#ifndef ISP2401
-static bool
-sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf,
-#else
static struct sh_css_hmm_buffer_record
*sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf,
-#endif
enum ia_css_buffer_type type,
hrt_address kernel_ptr)
{
int i;
struct sh_css_hmm_buffer_record *buffer_record = NULL;
-#ifndef ISP2401
- bool found_record = false;
-#else
struct sh_css_hmm_buffer_record *out_buffer_record = NULL;
-#endif
assert(h_vbuf != NULL);
assert((type > IA_CSS_BUFFER_TYPE_INVALID) && (type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE));
@@ -11320,21 +11075,13 @@ static struct sh_css_hmm_buffer_record
buffer_record->type = type;
buffer_record->h_vbuf = h_vbuf;
buffer_record->kernel_ptr = kernel_ptr;
-#ifndef ISP2401
- found_record = true;
-#else
out_buffer_record = buffer_record;
-#endif
break;
}
buffer_record++;
}
-#ifndef ISP2401
- return found_record;
-#else
return out_buffer_record;
-#endif
}
static struct sh_css_hmm_buffer_record
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c
index 34cc56f0b471..eecd8cf71951 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c
@@ -63,9 +63,6 @@ static const char *release_version = STR(irci_ecr-master_20150911_0724);
static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---";
struct ia_css_fw_info sh_css_sp_fw;
-#if defined(HAS_BL)
-struct ia_css_fw_info sh_css_bl_fw;
-#endif /* HAS_BL */
struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */
unsigned sh_css_num_binaries; /* This includes 1 SP binary */
@@ -95,12 +92,7 @@ setup_binary(struct ia_css_fw_info *fw, const char *fw_data, struct ia_css_fw_in
*sh_css_fw = *fw;
-#if defined(HRT_UNSCHED)
- sh_css_fw->blob.code = vmalloc(1);
-#else
sh_css_fw->blob.code = vmalloc(fw->blob.size);
-#endif
-
if (sh_css_fw->blob.code == NULL)
return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
@@ -137,12 +129,7 @@ sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, struct ia
bd->blob = blob;
bd->header = *bi;
- if ((bi->type == ia_css_isp_firmware) || (bi->type == ia_css_sp_firmware)
-#if defined(HAS_BL)
- || (bi->type == ia_css_bootloader_firmware)
-#endif /* HAS_BL */
- )
- {
+ if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) {
char *namebuffer;
int namelength = (int)strlen(name);
@@ -242,9 +229,9 @@ sh_css_load_firmware(const char *fw_data,
sh_css_num_binaries = file_header->binary_nr;
/* Only allocate memory for ISP blob info */
- if (sh_css_num_binaries > (NUM_OF_SPS + NUM_OF_BLS)) {
+ if (sh_css_num_binaries > NUM_OF_SPS) {
sh_css_blob_info = kmalloc(
- (sh_css_num_binaries - (NUM_OF_SPS + NUM_OF_BLS)) *
+ (sh_css_num_binaries - NUM_OF_SPS) *
sizeof(*sh_css_blob_info), GFP_KERNEL);
if (sh_css_blob_info == NULL)
return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
@@ -279,25 +266,16 @@ sh_css_load_firmware(const char *fw_data,
err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
if (err != IA_CSS_SUCCESS)
return err;
-#if defined(HAS_BL)
- } else if (bi->type == ia_css_bootloader_firmware) {
- if (i != BOOTLOADER_FIRMWARE)
- return IA_CSS_ERR_INTERNAL_ERROR;
- err = setup_binary(bi, fw_data, &sh_css_bl_fw, i);
- if (err != IA_CSS_SUCCESS)
- return err;
- IA_CSS_LOG("Bootloader binary recognized\n");
-#endif
} else {
- /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS+NUM_OF_BLS) are ISP firmware */
- if (i < (NUM_OF_SPS + NUM_OF_BLS))
+ /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
+ if (i < NUM_OF_SPS)
return IA_CSS_ERR_INTERNAL_ERROR;
if (bi->type != ia_css_isp_firmware)
return IA_CSS_ERR_INTERNAL_ERROR;
if (sh_css_blob_info == NULL) /* cannot happen but KW does not see this */
return IA_CSS_ERR_INTERNAL_ERROR;
- sh_css_blob_info[i-(NUM_OF_SPS + NUM_OF_BLS)] = bd;
+ sh_css_blob_info[i - NUM_OF_SPS] = bd;
}
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h
index e2b6f06ed099..5b2b78f96dc5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_internal.h
@@ -154,18 +154,11 @@
/* Number of SP's */
#define NUM_OF_SPS 1
-#if defined(HAS_BL)
-#define NUM_OF_BLS 1
-#else
#define NUM_OF_BLS 0
-#endif
/* Enum for order of Binaries */
enum sh_css_order_binaries {
SP_FIRMWARE = 0,
-#if defined(HAS_BL)
- BOOTLOADER_FIRMWARE,
-#endif
ISP_FIRMWARE
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c
deleted file mode 100644
index 37e954aea36f..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_irq.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-/* This file will contain the code to implement the functions declared in ia_css_irq.h
- and associated helper functions */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
index 7e3893c6c08a..36aaa3019a15 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
@@ -681,7 +681,7 @@ send_mipi_frames(struct ia_css_pipe *pipe)
unsigned int port = 0;
#endif
- IA_CSS_ENTER_PRIVATE("pipe=%d", pipe);
+ IA_CSS_ENTER_PRIVATE("pipe=%p", pipe);
assert(pipe != NULL);
assert(pipe->stream != NULL);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c
index 6de8472f1b07..237e38b2f0c1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mmu.c
@@ -13,16 +13,12 @@
*/
#include "ia_css_mmu.h"
-#ifdef ISP2401
#include "ia_css_mmu_private.h"
-#endif
#include <ia_css_debug.h>
#include "sh_css_sp.h"
#include "sh_css_firmware.h"
#include "sp.h"
-#ifdef ISP2401
#include "mmu_device.h"
-#endif
void
ia_css_mmu_invalidate_cache(void)
@@ -44,7 +40,6 @@ ia_css_mmu_invalidate_cache(void)
}
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_mmu_invalidate_cache() leave\n");
}
-#ifdef ISP2401
/* Deprecated, this is an HRT backend function (memory_access.h) */
void
@@ -59,4 +54,3 @@ sh_css_mmu_set_page_table_base_index(hrt_data base_index)
}
IA_CSS_LEAVE_PRIVATE("");
}
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
index 561f4a7236f7..48224370b8bf 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
@@ -3356,15 +3356,8 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe,
}
/* Free any existing tables. */
-#ifndef ISP2401
- if (pipe->scaler_pp_lut != mmgr_NULL) {
- hmm_free(pipe->scaler_pp_lut);
- pipe->scaler_pp_lut = mmgr_NULL;
- }
-#else
sh_css_params_free_gdc_lut(pipe->scaler_pp_lut);
pipe->scaler_pp_lut = mmgr_NULL;
-#endif
#ifndef ISP2401
if (store) {
@@ -3375,7 +3368,7 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe,
#endif
if (pipe->scaler_pp_lut == mmgr_NULL) {
#ifndef ISP2401
- IA_CSS_LEAVE("lut(%p) err=%d", pipe->scaler_pp_lut, err);
+ IA_CSS_LEAVE("lut(%u) err=%d", pipe->scaler_pp_lut, err);
return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
#else
ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
@@ -3397,7 +3390,7 @@ enum ia_css_err ia_css_pipe_set_bci_scaler_lut(struct ia_css_pipe *pipe,
#endif
}
- IA_CSS_LEAVE("lut(%p) err=%d", pipe->scaler_pp_lut, err);
+ IA_CSS_LEAVE("lut(%u) err=%d", pipe->scaler_pp_lut, err);
return err;
}
@@ -3437,7 +3430,7 @@ enum ia_css_err sh_css_params_map_and_store_default_gdc_lut(void)
mmgr_store(default_gdc_lut, (int *)interleaved_lut_temp,
sizeof(zoom_table));
- IA_CSS_LEAVE_PRIVATE("lut(%p) err=%d", default_gdc_lut, err);
+ IA_CSS_LEAVE_PRIVATE("lut(%u) err=%d", default_gdc_lut, err);
return err;
}
@@ -3445,15 +3438,8 @@ void sh_css_params_free_default_gdc_lut(void)
{
IA_CSS_ENTER_PRIVATE("void");
-#ifndef ISP2401
- if (default_gdc_lut != mmgr_NULL) {
- hmm_free(default_gdc_lut);
- default_gdc_lut = mmgr_NULL;
- }
-#else
sh_css_params_free_gdc_lut(default_gdc_lut);
default_gdc_lut = mmgr_NULL;
-#endif
IA_CSS_LEAVE_PRIVATE("void");
@@ -3859,7 +3845,7 @@ sh_css_param_update_isp_params(struct ia_css_pipe *curr_pipe,
/* When API change is implemented making good distinction between
* stream config and pipe config this skipping code can be moved out of the #ifdef */
if (pipe_in && (pipe != pipe_in)) {
- IA_CSS_LOG("skipping pipe %x", pipe);
+ IA_CSS_LOG("skipping pipe %p", pipe);
continue;
}
@@ -4590,7 +4576,7 @@ free_ia_css_isp_parameter_set_info(
unsigned int i;
hrt_vaddress *addrs = (hrt_vaddress *)&isp_params_info.mem_map;
- IA_CSS_ENTER_PRIVATE("ptr = %p", ptr);
+ IA_CSS_ENTER_PRIVATE("ptr = %u", ptr);
/* sanity check - ptr must be valid */
if (!ia_css_refcount_is_valid(ptr)) {
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c
index 57295397da3e..05eeff58a229 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c
@@ -43,6 +43,7 @@ struct hmm_bo_device bo_device;
struct hmm_pool dynamic_pool;
struct hmm_pool reserved_pool;
static ia_css_ptr dummy_ptr;
+static bool hmm_initialized;
struct _hmm_mem_stat hmm_mem_stat;
/* p: private
@@ -186,6 +187,8 @@ int hmm_init(void)
if (ret)
dev_err(atomisp_dev, "hmm_bo_device_init failed.\n");
+ hmm_initialized = true;
+
/*
* As hmm use NULL to indicate invalid ISP virtual address,
* and ISP_VM_START is defined to 0 too, so we allocate
@@ -193,7 +196,7 @@ int hmm_init(void)
* at the beginning, to avoid hmm_alloc return 0 in the
* further allocation.
*/
- dummy_ptr = hmm_alloc(1, HMM_BO_PRIVATE, 0, 0, HMM_UNCACHED);
+ dummy_ptr = hmm_alloc(1, HMM_BO_PRIVATE, 0, NULL, HMM_UNCACHED);
if (!ret) {
ret = sysfs_create_group(&atomisp_dev->kobj,
@@ -217,6 +220,7 @@ void hmm_cleanup(void)
dummy_ptr = 0;
hmm_bo_device_exit(&bo_device);
+ hmm_initialized = false;
}
ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type,
@@ -229,7 +233,7 @@ ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type,
/* Check if we are initialized. In the ideal world we wouldn't need
this but we can tackle it once the driver is a lot cleaner */
- if (!dummy_ptr)
+ if (!hmm_initialized)
hmm_init();
/*Get page number from size*/
pgnr = size_to_pgnr_ceil(bytes);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c b/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c
index 7dff22f59e29..2e78976bb2ac 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/hrt/hive_isp_css_mm_hrt.c
@@ -55,7 +55,7 @@ static ia_css_ptr __hrt_isp_css_mm_alloc(size_t bytes, void *userptr,
if (type == HRT_USR_PTR) {
if (userptr == NULL)
return hmm_alloc(bytes, HMM_BO_PRIVATE, 0,
- 0, cached);
+ NULL, cached);
else {
if (num_pages < ((__page_align(bytes)) >> PAGE_SHIFT))
dev_err(atomisp_dev,
@@ -94,7 +94,7 @@ ia_css_ptr hrt_isp_css_mm_alloc_user_ptr(size_t bytes, void *userptr,
ia_css_ptr hrt_isp_css_mm_alloc_cached(size_t bytes)
{
if (my_userptr == NULL)
- return hmm_alloc(bytes, HMM_BO_PRIVATE, 0, 0,
+ return hmm_alloc(bytes, HMM_BO_PRIVATE, 0, NULL,
HMM_CACHED);
else {
if (my_num_pages < ((__page_align(bytes)) >> PAGE_SHIFT))
diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
index 5b4506a71126..edaae93af8f9 100644
--- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
@@ -51,7 +51,7 @@ struct gmin_subdev {
static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS];
-static enum { PMIC_UNSET = 0, PMIC_REGULATOR, PMIC_AXP, PMIC_TI ,
+static enum { PMIC_UNSET = 0, PMIC_REGULATOR, PMIC_AXP, PMIC_TI,
PMIC_CRYSTALCOVE } pmic_id;
/* The atomisp uses type==0 for the end-of-list marker, so leave space. */
@@ -119,7 +119,7 @@ static int af_power_ctrl(struct v4l2_subdev *subdev, int flag)
/*
* The power here is used for dw9817,
* regulator is from rear sensor
- */
+ */
if (gs->v2p8_vcm_reg) {
if (flag)
return regulator_enable(gs->v2p8_vcm_reg);
@@ -152,13 +152,13 @@ const struct camera_af_platform_data *camera_get_af_platform_data(void)
EXPORT_SYMBOL_GPL(camera_get_af_platform_data);
int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
- struct camera_sensor_platform_data *plat_data,
- enum intel_v4l2_subdev_type type)
+ struct camera_sensor_platform_data *plat_data,
+ enum intel_v4l2_subdev_type type)
{
int i;
struct i2c_board_info *bi;
struct gmin_subdev *gs;
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct acpi_device *adev;
dev_info(&client->dev, "register atomisp i2c module type %d\n", type);
@@ -167,12 +167,13 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
* uses ACPI runtime power management for camera devices, but
* we don't. Disable it, or else the rails will be needlessly
* tickled during suspend/resume. This has caused power and
- * performance issues on multiple devices. */
+ * performance issues on multiple devices.
+ */
adev = ACPI_COMPANION(&client->dev);
if (adev)
adev->power.flags.power_resources = 0;
- for (i=0; i < MAX_SUBDEVS; i++)
+ for (i = 0; i < MAX_SUBDEVS; i++)
if (!pdata.subdevs[i].type)
break;
@@ -182,7 +183,8 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev,
/* Note subtlety of initialization order: at the point where
* this registration API gets called, the platform data
* callbacks have probably already been invoked, so the
- * gmin_subdev struct is already initialized for us. */
+ * gmin_subdev struct is already initialized for us.
+ */
gs = find_gmin_subdev(subdev);
pdata.subdevs[i].type = type;
@@ -206,8 +208,10 @@ struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter,
struct i2c_board_info *board_info)
{
int i;
- for (i=0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) {
+
+ for (i = 0; i < MAX_SUBDEVS && pdata.subdevs[i].type; i++) {
struct intel_v4l2_subdev_table *sd = &pdata.subdevs[i];
+
if (sd->v4l2_subdev.i2c_adapter_id == adapter->nr &&
sd->v4l2_subdev.board_info.addr == board_info->addr)
return sd->subdev;
@@ -261,7 +265,8 @@ static const struct gmin_cfg_var ffrd8_vars[] = {
};
/* Cribbed from MCG defaults in the mt9m114 driver, not actually verified
- * vs. T100 hardware */
+ * vs. T100 hardware
+ */
static const struct gmin_cfg_var t100_vars[] = {
{ "INT33F0:00_CsiPort", "0" },
{ "INT33F0:00_CsiLanes", "1" },
@@ -270,45 +275,45 @@ static const struct gmin_cfg_var t100_vars[] = {
};
static const struct gmin_cfg_var mrd7_vars[] = {
- {"INT33F8:00_CamType", "1"},
- {"INT33F8:00_CsiPort", "1"},
- {"INT33F8:00_CsiLanes","2"},
- {"INT33F8:00_CsiFmt","13"},
- {"INT33F8:00_CsiBayer", "0"},
- {"INT33F8:00_CamClk", "0"},
- {"INT33F9:00_CamType", "1"},
- {"INT33F9:00_CsiPort", "0"},
- {"INT33F9:00_CsiLanes","1"},
- {"INT33F9:00_CsiFmt","13"},
- {"INT33F9:00_CsiBayer", "0"},
- {"INT33F9:00_CamClk", "1"},
- {},
+ {"INT33F8:00_CamType", "1"},
+ {"INT33F8:00_CsiPort", "1"},
+ {"INT33F8:00_CsiLanes", "2"},
+ {"INT33F8:00_CsiFmt", "13"},
+ {"INT33F8:00_CsiBayer", "0"},
+ {"INT33F8:00_CamClk", "0"},
+ {"INT33F9:00_CamType", "1"},
+ {"INT33F9:00_CsiPort", "0"},
+ {"INT33F9:00_CsiLanes", "1"},
+ {"INT33F9:00_CsiFmt", "13"},
+ {"INT33F9:00_CsiBayer", "0"},
+ {"INT33F9:00_CamClk", "1"},
+ {},
};
static const struct gmin_cfg_var ecs7_vars[] = {
- {"INT33BE:00_CsiPort", "1"},
- {"INT33BE:00_CsiLanes","2"},
- {"INT33BE:00_CsiFmt","13"},
- {"INT33BE:00_CsiBayer", "2"},
- {"INT33BE:00_CamClk", "0"},
- {"INT33F0:00_CsiPort", "0"},
- {"INT33F0:00_CsiLanes","1"},
- {"INT33F0:00_CsiFmt","13"},
- {"INT33F0:00_CsiBayer", "0"},
- {"INT33F0:00_CamClk", "1"},
- {"gmin_V2P8GPIO","402"},
- {},
+ {"INT33BE:00_CsiPort", "1"},
+ {"INT33BE:00_CsiLanes", "2"},
+ {"INT33BE:00_CsiFmt", "13"},
+ {"INT33BE:00_CsiBayer", "2"},
+ {"INT33BE:00_CamClk", "0"},
+ {"INT33F0:00_CsiPort", "0"},
+ {"INT33F0:00_CsiLanes", "1"},
+ {"INT33F0:00_CsiFmt", "13"},
+ {"INT33F0:00_CsiBayer", "0"},
+ {"INT33F0:00_CamClk", "1"},
+ {"gmin_V2P8GPIO", "402"},
+ {},
};
static const struct gmin_cfg_var i8880_vars[] = {
- {"XXOV2680:00_CsiPort", "1"},
- {"XXOV2680:00_CsiLanes","1"},
- {"XXOV2680:00_CamClk","0"},
- {"XXGC0310:00_CsiPort", "0"},
- {"XXGC0310:00_CsiLanes", "1"},
- {"XXGC0310:00_CamClk", "1"},
- {},
+ {"XXOV2680:00_CsiPort", "1"},
+ {"XXOV2680:00_CsiLanes", "1"},
+ {"XXOV2680:00_CamClk", "0"},
+ {"XXGC0310:00_CsiPort", "0"},
+ {"XXGC0310:00_CsiLanes", "1"},
+ {"XXGC0310:00_CamClk", "1"},
+ {},
};
static const struct {
@@ -317,9 +322,9 @@ static const struct {
} hard_vars[] = {
{ "BYT-T FFD8", ffrd8_vars },
{ "T100TA", t100_vars },
- { "MRD7", mrd7_vars },
- { "ST70408", ecs7_vars },
- { "VTA0803", i8880_vars },
+ { "MRD7", mrd7_vars },
+ { "ST70408", ecs7_vars },
+ { "VTA0803", i8880_vars },
};
@@ -343,19 +348,17 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
{
int i, ret;
struct device *dev;
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
-
- if (!pmic_id) {
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
- pmic_id = PMIC_REGULATOR;
- }
+ if (!pmic_id)
+ pmic_id = PMIC_REGULATOR;
if (!client)
return NULL;
dev = &client->dev;
- for (i=0; i < MAX_SUBDEVS && gmin_subdevs[i].subdev; i++)
+ for (i = 0; i < MAX_SUBDEVS && gmin_subdevs[i].subdev; i++)
;
if (i >= MAX_SUBDEVS)
return NULL;
@@ -401,7 +404,8 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
* API is broken with the current drivers, returning
* "1" for a regulator that will then emit a
* "unbalanced disable" WARNing if we try to disable
- * it. */
+ * it.
+ */
}
return &gmin_subdevs[i];
@@ -410,7 +414,8 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev)
{
int i;
- for (i=0; i < MAX_SUBDEVS; i++)
+
+ for (i = 0; i < MAX_SUBDEVS; i++)
if (gmin_subdevs[i].subdev == subdev)
return &gmin_subdevs[i];
return gmin_subdev_add(subdev);
@@ -419,6 +424,7 @@ static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev)
static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
+
if (gs && gs->gpio0) {
gpiod_set_value(gs->gpio0, on);
return 0;
@@ -429,6 +435,7 @@ static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on)
static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
+
if (gs && gs->gpio1) {
gpiod_set_value(gs->gpio1, on);
return 0;
@@ -436,7 +443,7 @@ static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on)
return -EINVAL;
}
-int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
+static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
@@ -455,7 +462,8 @@ int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
return -EINVAL;
}
-int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
+
+static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
int ret;
@@ -481,7 +489,7 @@ int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
gpio_set_value(v1p8_gpio, on);
if (gs->v1p8_reg) {
- regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000);
+ regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000);
if (on)
return regulator_enable(gs->v1p8_reg);
else
@@ -491,7 +499,7 @@ int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
return -EINVAL;
}
-int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
+static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
int ret;
@@ -517,7 +525,7 @@ int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
gpio_set_value(v2p8_gpio, on);
if (gs->v2p8_reg) {
- regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000);
+ regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000);
if (on)
return regulator_enable(gs->v2p8_reg);
else
@@ -527,10 +535,11 @@ int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
return -EINVAL;
}
-int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on)
+static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on)
{
int ret = 0;
struct gmin_subdev *gs = find_gmin_subdev(subdev);
+
if (on)
ret = vlv2_plat_set_clock_freq(gs->clock_num, gs->clock_src);
if (ret)
@@ -595,6 +604,7 @@ struct camera_sensor_platform_data *gmin_camera_platform_data(
enum atomisp_bayer_order csi_bayer)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
+
gs->csi_fmt = csi_format;
gs->csi_bayer = csi_bayer;
@@ -617,30 +627,31 @@ EXPORT_SYMBOL_GPL(atomisp_gmin_register_vcm_control);
/* Retrieves a device-specific configuration variable. The dev
* argument should be a device with an ACPI companion, as all
- * configuration is based on firmware ID. */
-int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *out_len)
+ * configuration is based on firmware ID.
+ */
+int gmin_get_config_var(struct device *dev, const char *var, char *out,
+ size_t *out_len)
{
char var8[CFG_VAR_NAME_MAX];
efi_char16_t var16[CFG_VAR_NAME_MAX];
struct efivar_entry *ev;
- u32 efiattr_dummy;
int i, j, ret;
- unsigned long efilen;
- if (dev && ACPI_COMPANION(dev))
- dev = &ACPI_COMPANION(dev)->dev;
+ if (dev && ACPI_COMPANION(dev))
+ dev = &ACPI_COMPANION(dev)->dev;
- if (dev)
- ret = snprintf(var8, sizeof(var8), "%s_%s", dev_name(dev), var);
- else
- ret = snprintf(var8, sizeof(var8), "gmin_%s", var);
+ if (dev)
+ ret = snprintf(var8, sizeof(var8), "%s_%s", dev_name(dev), var);
+ else
+ ret = snprintf(var8, sizeof(var8), "gmin_%s", var);
if (ret < 0 || ret >= sizeof(var8) - 1)
return -EINVAL;
/* First check a hard-coded list of board-specific variables.
* Some device firmwares lack the ability to set EFI variables at
- * runtime. */
+ * runtime.
+ */
for (i = 0; i < ARRAY_SIZE(hard_vars); i++) {
if (dmi_match(DMI_BOARD_NAME, hard_vars[i].dmi_board_name)) {
for (j = 0; hard_vars[i].vars[j].name; j++) {
@@ -665,7 +676,8 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *
}
/* Our variable names are ASCII by construction, but EFI names
- * are wide chars. Convert and zero-pad. */
+ * are wide chars. Convert and zero-pad.
+ */
memset(var16, 0, sizeof(var16));
for (i = 0; i < sizeof(var8) && var8[i]; i++)
var16[i] = var8[i];
@@ -678,21 +690,25 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *
* implementation simply uses VariableName and VendorGuid from
* the struct and ignores the rest, but it seems like there
* ought to be an "official" efivar_entry registered
- * somewhere? */
+ * somewhere?
+ */
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
if (!ev)
return -ENOMEM;
memcpy(&ev->var.VariableName, var16, sizeof(var16));
ev->var.VendorGuid = GMIN_CFG_VAR_EFI_GUID;
-
- efilen = *out_len;
- ret = efivar_entry_get(ev, &efiattr_dummy, &efilen, out);
+ ev->var.DataSize = *out_len;
+
+ ret = efivar_entry_get(ev, &ev->var.Attributes,
+ &ev->var.DataSize, ev->var.Data);
+ if (ret == 0) {
+ memcpy(out, ev->var.Data, ev->var.DataSize);
+ *out_len = ev->var.DataSize;
+ } else if (dev) {
+ dev_warn(dev, "Failed to find gmin variable %s\n", var8);
+ }
kfree(ev);
- *out_len = efilen;
-
- if (ret)
- dev_warn(dev, "Failed to find gmin variable %s\n", var8);
return ret;
}
@@ -718,38 +734,39 @@ EXPORT_SYMBOL_GPL(gmin_get_var_int);
int camera_sensor_csi(struct v4l2_subdev *sd, u32 port,
u32 lanes, u32 format, u32 bayer_order, int flag)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct camera_mipi_info *csi = NULL;
-
- if (flag) {
- csi = kzalloc(sizeof(*csi), GFP_KERNEL);
- if (!csi) {
- dev_err(&client->dev, "out of memory\n");
- return -ENOMEM;
- }
- csi->port = port;
- csi->num_lanes = lanes;
- csi->input_format = format;
- csi->raw_bayer_order = bayer_order;
- v4l2_set_subdev_hostdata(sd, (void *)csi);
- csi->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED;
- csi->metadata_effective_width = NULL;
- dev_info(&client->dev,
- "camera pdata: port: %d lanes: %d order: %8.8x\n",
- port, lanes, bayer_order);
- } else {
- csi = v4l2_get_subdev_hostdata(sd);
- kfree(csi);
- }
-
- return 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct camera_mipi_info *csi = NULL;
+
+ if (flag) {
+ csi = kzalloc(sizeof(*csi), GFP_KERNEL);
+ if (!csi) {
+ dev_err(&client->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ csi->port = port;
+ csi->num_lanes = lanes;
+ csi->input_format = format;
+ csi->raw_bayer_order = bayer_order;
+ v4l2_set_subdev_hostdata(sd, (void *)csi);
+ csi->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED;
+ csi->metadata_effective_width = NULL;
+ dev_info(&client->dev,
+ "camera pdata: port: %d lanes: %d order: %8.8x\n",
+ port, lanes, bayer_order);
+ } else {
+ csi = v4l2_get_subdev_hostdata(sd);
+ kfree(csi);
+ }
+
+ return 0;
}
EXPORT_SYMBOL_GPL(camera_sensor_csi);
/* PCI quirk: The BYT ISP advertises PCI runtime PM but it doesn't
* work. Disable so the kernel framework doesn't hang the device
* trying. The driver itself does direct calls to the PUNIT to manage
- * ISP power. */
+ * ISP power.
+ */
static void isp_pm_cap_fixup(struct pci_dev *dev)
{
dev_info(&dev->dev, "Disabling PCI power management on camera ISP\n");
diff --git a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c
index a6c0f5f8c3f8..cd452cc20fea 100644
--- a/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c
+++ b/drivers/staging/media/atomisp/platform/intel-mid/intel_mid_pcihelpers.c
@@ -5,7 +5,8 @@
/* G-Min addition: "platform_is()" lives in intel_mid_pm.h in the MCG
* tree, but it's just platform ID info and we don't want to pull in
- * the whole SFI-based PM architecture. */
+ * the whole SFI-based PM architecture.
+ */
#define INTEL_ATOM_MRST 0x26
#define INTEL_ATOM_MFLD 0x27
#define INTEL_ATOM_CLV 0x35
@@ -22,7 +23,7 @@
#endif
static inline int platform_is(u8 model)
{
- return (boot_cpu_data.x86_model == model);
+ return (boot_cpu_data.x86_model == model);
}
#include "../../include/asm/intel_mid_pcihelpers.h"
@@ -32,7 +33,6 @@ static DEFINE_SPINLOCK(msgbus_lock);
static struct pci_dev *pci_root;
static struct pm_qos_request pm_qos;
-int qos;
#define DW_I2C_NEED_QOS (platform_is(INTEL_ATOM_BYT))
@@ -136,8 +136,8 @@ u32 intel_mid_msgbus_read32(u8 port, u32 addr)
return data;
}
-
EXPORT_SYMBOL(intel_mid_msgbus_read32);
+
void intel_mid_msgbus_write32(u8 port, u32 addr, u32 data)
{
unsigned long irq_flags;
@@ -171,8 +171,8 @@ EXPORT_SYMBOL(intel_mid_soc_stepping);
static bool is_south_complex_device(struct pci_dev *dev)
{
- unsigned base_class = dev->class >> 16;
- unsigned sub_class = (dev->class & SUB_CLASS_MASK) >> 8;
+ unsigned int base_class = dev->class >> 16;
+ unsigned int sub_class = (dev->class & SUB_CLASS_MASK) >> 8;
/* other than camera, pci bridges and display,
* everything else are south complex devices.
diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
index 18186d0fa1a6..370ecb959543 100644
--- a/drivers/staging/media/cxd2099/cxd2099.c
+++ b/drivers/staging/media/cxd2099/cxd2099.c
@@ -473,7 +473,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
{
struct cxd *ci = ca->data;
- dev_info(&ci->i2c->dev, "slot_shutdown\n");
+ dev_info(&ci->i2c->dev, "%s\n", __func__);
mutex_lock(&ci->lock);
write_regm(ci, 0x09, 0x08, 0x08);
write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
@@ -564,7 +564,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
campoll(ci);
mutex_unlock(&ci->lock);
- dev_info(&ci->i2c->dev, "read_data\n");
+ dev_info(&ci->i2c->dev, "%s\n", __func__);
if (!ci->dr)
return 0;
@@ -584,7 +584,7 @@ static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
struct cxd *ci = ca->data;
mutex_lock(&ci->lock);
- dev_info(&ci->i2c->dev, "write_data %d\n", ecount);
+ dev_info(&ci->i2c->dev, "%s %d\n", __func__, ecount);
write_reg(ci, 0x0d, ecount >> 8);
write_reg(ci, 0x0e, ecount & 0xff);
write_block(ci, 0x11, ebuf, ecount);
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
new file mode 100644
index 000000000000..7eff50bcea39
--- /dev/null
+++ b/drivers/staging/media/imx/Kconfig
@@ -0,0 +1,21 @@
+config VIDEO_IMX_MEDIA
+ tristate "i.MX5/6 V4L2 media core driver"
+ depends on MEDIA_CONTROLLER && VIDEO_V4L2 && ARCH_MXC && IMX_IPUV3_CORE
+ select V4L2_FWNODE
+ ---help---
+ Say yes here to enable support for video4linux media controller
+ driver for the i.MX5/6 SOC.
+
+if VIDEO_IMX_MEDIA
+menu "i.MX5/6 Media Sub devices"
+
+config VIDEO_IMX_CSI
+ tristate "i.MX5/6 Camera Sensor Interface driver"
+ depends on VIDEO_IMX_MEDIA && VIDEO_DEV && I2C
+ select VIDEOBUF2_DMA_CONTIG
+ default y
+ ---help---
+ A video4linux camera sensor interface driver for i.MX5/6.
+
+endmenu
+endif
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
new file mode 100644
index 000000000000..3569625b6305
--- /dev/null
+++ b/drivers/staging/media/imx/Makefile
@@ -0,0 +1,12 @@
+imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o
+imx-media-common-objs := imx-media-utils.o imx-media-fim.o
+imx-media-ic-objs := imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o
+
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-capture.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-vdic.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-ic.o
+
+obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o
+obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO
new file mode 100644
index 000000000000..0bee3132b26f
--- /dev/null
+++ b/drivers/staging/media/imx/TODO
@@ -0,0 +1,23 @@
+
+- Clean up and move the ov5642 subdev driver to drivers/media/i2c, or
+ merge support for OV5642 into drivers/media/i2c/ov5640.c, and create
+ the binding docs for it.
+
+- The Frame Interval Monitor could be exported to v4l2-core for
+ general use.
+
+- At driver load time, the device-tree node that is the original source
+ (the "sensor"), is parsed to record its media bus configuration, and
+ this info is required in imx-media-csi.c to setup the CSI.
+ Laurent Pinchart argues that instead the CSI subdev should call its
+ neighbor's g_mbus_config op (which should be propagated if necessary)
+ to get this info. However Hans Verkuil is planning to remove the
+ g_mbus_config op. For now this driver uses the parsed DT mbus config
+ method until this issue is resolved.
+
+- This media driver supports inheriting V4L2 controls to the
+ video capture devices, from the subdevices in the capture device's
+ pipeline. The controls for each capture device are updated in the
+ link_notify callback when the pipeline is modified. It should be
+ decided whether this feature is useful enough to make it generally
+ available by exporting to v4l2-core.
diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c
new file mode 100644
index 000000000000..cfdd4900a3be
--- /dev/null
+++ b/drivers/staging/media/imx/imx-ic-common.c
@@ -0,0 +1,113 @@
+/*
+ * V4L2 Image Converter Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "imx-media.h"
+#include "imx-ic.h"
+
+#define IC_TASK_PRP IC_NUM_TASKS
+#define IC_NUM_OPS (IC_NUM_TASKS + 1)
+
+static struct imx_ic_ops *ic_ops[IC_NUM_OPS] = {
+ [IC_TASK_PRP] = &imx_ic_prp_ops,
+ [IC_TASK_ENCODER] = &imx_ic_prpencvf_ops,
+ [IC_TASK_VIEWFINDER] = &imx_ic_prpencvf_ops,
+};
+
+static int imx_ic_probe(struct platform_device *pdev)
+{
+ struct imx_media_internal_sd_platformdata *pdata;
+ struct imx_ic_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, &priv->sd);
+ priv->dev = &pdev->dev;
+
+ /* get our ipu_id, grp_id and IC task id */
+ pdata = priv->dev->platform_data;
+ priv->ipu_id = pdata->ipu_id;
+ switch (pdata->grp_id) {
+ case IMX_MEDIA_GRP_ID_IC_PRP:
+ priv->task_id = IC_TASK_PRP;
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRPENC:
+ priv->task_id = IC_TASK_ENCODER;
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRPVF:
+ priv->task_id = IC_TASK_VIEWFINDER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ v4l2_subdev_init(&priv->sd, ic_ops[priv->task_id]->subdev_ops);
+ v4l2_set_subdevdata(&priv->sd, priv);
+ priv->sd.internal_ops = ic_ops[priv->task_id]->internal_ops;
+ priv->sd.entity.ops = ic_ops[priv->task_id]->entity_ops;
+ priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ priv->sd.dev = &pdev->dev;
+ priv->sd.owner = THIS_MODULE;
+ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ priv->sd.grp_id = pdata->grp_id;
+ strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
+
+ ret = ic_ops[priv->task_id]->init(priv);
+ if (ret)
+ return ret;
+
+ ret = v4l2_async_register_subdev(&priv->sd);
+ if (ret)
+ ic_ops[priv->task_id]->remove(priv);
+
+ return ret;
+}
+
+static int imx_ic_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct imx_ic_priv *priv = container_of(sd, struct imx_ic_priv, sd);
+
+ v4l2_info(sd, "Removing\n");
+
+ ic_ops[priv->task_id]->remove(priv);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct platform_device_id imx_ic_ids[] = {
+ { .name = "imx-ipuv3-ic" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, imx_ic_ids);
+
+static struct platform_driver imx_ic_driver = {
+ .probe = imx_ic_probe,
+ .remove = imx_ic_remove,
+ .id_table = imx_ic_ids,
+ .driver = {
+ .name = "imx-ipuv3-ic",
+ },
+};
+module_platform_driver(imx_ic_driver);
+
+MODULE_DESCRIPTION("i.MX IC subdev driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-ic");
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
new file mode 100644
index 000000000000..c2bb5ef2acb4
--- /dev/null
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -0,0 +1,518 @@
+/*
+ * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of video frames from the CSI or VDIC,
+ * which are routed directly to the Image Converter preprocess tasks,
+ * for resizing, colorspace conversion, and rotation.
+ *
+ * Copyright (c) 2012-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/imx.h>
+#include "imx-media.h"
+#include "imx-ic.h"
+
+/*
+ * Min/Max supported width and heights.
+ */
+#define MIN_W 176
+#define MIN_H 144
+#define MAX_W 4096
+#define MAX_H 4096
+#define W_ALIGN 4 /* multiple of 16 pixels */
+#define H_ALIGN 1 /* multiple of 2 lines */
+#define S_ALIGN 1 /* multiple of 2 */
+
+struct prp_priv {
+ struct imx_media_dev *md;
+ struct imx_ic_priv *ic_priv;
+ struct media_pad pad[PRP_NUM_PADS];
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ /* IPU units we require */
+ struct ipu_soc *ipu;
+
+ struct v4l2_subdev *src_sd;
+ struct v4l2_subdev *sink_sd_prpenc;
+ struct v4l2_subdev *sink_sd_prpvf;
+
+ /* the CSI id at link validate */
+ int csi_id;
+
+ struct v4l2_mbus_framefmt format_mbus;
+ struct v4l2_fract frame_interval;
+
+ int stream_count;
+};
+
+static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
+{
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+
+ return ic_priv->prp_priv;
+}
+
+static int prp_start(struct prp_priv *priv)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ bool src_is_vdic;
+
+ priv->ipu = priv->md->ipu[ic_priv->ipu_id];
+
+ /* set IC to receive from CSI or VDI depending on source */
+ src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC);
+
+ ipu_set_ic_src_mux(priv->ipu, priv->csi_id, src_is_vdic);
+
+ return 0;
+}
+
+static void prp_stop(struct prp_priv *priv)
+{
+}
+
+static struct v4l2_mbus_framefmt *
+__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad);
+ else
+ return &priv->format_mbus;
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int prp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_mbus_framefmt *infmt;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ switch (code->pad) {
+ case PRP_SINK_PAD:
+ ret = imx_media_enum_ipu_format(&code->code, code->index,
+ CS_SEL_ANY);
+ break;
+ case PRP_SRC_PAD_PRPENC:
+ case PRP_SRC_PAD_PRPVF:
+ if (code->index != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, code->which);
+ code->code = infmt->code;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= PRP_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdformat->format = *fmt;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_mbus_framefmt *fmt, *infmt;
+ const struct imx_media_pixfmt *cc;
+ int ret = 0;
+ u32 code;
+
+ if (sdformat->pad >= PRP_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which);
+
+ switch (sdformat->pad) {
+ case PRP_SINK_PAD:
+ v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
+ W_ALIGN, &sdformat->format.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+
+ cc = imx_media_find_ipu_format(sdformat->format.code,
+ CS_SEL_ANY);
+ if (!cc) {
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
+ cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ sdformat->format.code = cc->codes[0];
+ }
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
+ break;
+ case PRP_SRC_PAD_PRPENC:
+ case PRP_SRC_PAD_PRPVF:
+ /* Output pads mirror input pad */
+ sdformat->format = *infmt;
+ break;
+ }
+
+ fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ *fmt = sdformat->format;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+ struct prp_priv *priv = ic_priv->prp_priv;
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ mutex_lock(&priv->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SINK) {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (priv->sink_sd_prpenc && (remote_sd->grp_id &
+ IMX_MEDIA_GRP_ID_VDIC)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ priv->src_sd = remote_sd;
+ } else {
+ priv->src_sd = NULL;
+ }
+
+ goto out;
+ }
+
+ /* this is a source pad */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ switch (local->index) {
+ case PRP_SRC_PAD_PRPENC:
+ if (priv->sink_sd_prpenc) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (priv->src_sd && (priv->src_sd->grp_id &
+ IMX_MEDIA_GRP_ID_VDIC)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ priv->sink_sd_prpenc = remote_sd;
+ break;
+ case PRP_SRC_PAD_PRPVF:
+ if (priv->sink_sd_prpvf) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->sink_sd_prpvf = remote_sd;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ } else {
+ switch (local->index) {
+ case PRP_SRC_PAD_PRPENC:
+ priv->sink_sd_prpenc = NULL;
+ break;
+ case PRP_SRC_PAD_PRPVF:
+ priv->sink_sd_prpvf = NULL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+ struct prp_priv *priv = ic_priv->prp_priv;
+ struct imx_media_subdev *csi;
+ int ret;
+
+ ret = v4l2_subdev_link_validate_default(sd, link,
+ source_fmt, sink_fmt);
+ if (ret)
+ return ret;
+
+ csi = imx_media_find_upstream_subdev(priv->md, &ic_priv->sd.entity,
+ IMX_MEDIA_GRP_ID_CSI);
+ if (IS_ERR(csi))
+ csi = NULL;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC) {
+ /*
+ * the ->PRPENC link cannot be enabled if the source
+ * is the VDIC
+ */
+ if (priv->sink_sd_prpenc)
+ ret = -EINVAL;
+ goto out;
+ } else {
+ /* the source is a CSI */
+ if (!csi) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (csi) {
+ switch (csi->sd->grp_id) {
+ case IMX_MEDIA_GRP_ID_CSI0:
+ priv->csi_id = 0;
+ break;
+ case IMX_MEDIA_GRP_ID_CSI1:
+ priv->csi_id = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ } else {
+ priv->csi_id = 0;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+ struct prp_priv *priv = ic_priv->prp_priv;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (priv->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(ic_priv->dev, "stream %s\n", enable ? "ON" : "OFF");
+
+ if (enable)
+ ret = prp_start(priv);
+ else
+ prp_stop(priv);
+ if (ret)
+ goto out;
+
+ /* start/stop upstream */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret) {
+ if (enable)
+ prp_stop(priv);
+ goto out;
+ }
+
+update_count:
+ priv->stream_count += enable ? 1 : -1;
+ if (priv->stream_count < 0)
+ priv->stream_count = 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+
+ if (fi->pad >= PRP_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+ fi->interval = priv->frame_interval;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int prp_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+
+ if (fi->pad >= PRP_NUM_PADS)
+ return -EINVAL;
+
+ /* No limits on frame interval */
+ mutex_lock(&priv->lock);
+ priv->frame_interval = fi->interval;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int prp_registered(struct v4l2_subdev *sd)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ int i, ret;
+ u32 code;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ for (i = 0; i < PRP_NUM_PADS; i++) {
+ priv->pad[i].flags = (i == PRP_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+ }
+
+ /* init default frame interval */
+ priv->frame_interval.numerator = 1;
+ priv->frame_interval.denominator = 30;
+
+ /* set a default mbus format */
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ ret = imx_media_init_mbus_fmt(&priv->format_mbus, 640, 480, code,
+ V4L2_FIELD_NONE, NULL);
+ if (ret)
+ return ret;
+
+ return media_entity_pads_init(&sd->entity, PRP_NUM_PADS, priv->pad);
+}
+
+static const struct v4l2_subdev_pad_ops prp_pad_ops = {
+ .enum_mbus_code = prp_enum_mbus_code,
+ .get_fmt = prp_get_fmt,
+ .set_fmt = prp_set_fmt,
+ .link_validate = prp_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops prp_video_ops = {
+ .g_frame_interval = prp_g_frame_interval,
+ .s_frame_interval = prp_s_frame_interval,
+ .s_stream = prp_s_stream,
+};
+
+static const struct media_entity_operations prp_entity_ops = {
+ .link_setup = prp_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_ops prp_subdev_ops = {
+ .video = &prp_video_ops,
+ .pad = &prp_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops prp_internal_ops = {
+ .registered = prp_registered,
+};
+
+static int prp_init(struct imx_ic_priv *ic_priv)
+{
+ struct prp_priv *priv;
+
+ priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ ic_priv->prp_priv = priv;
+ priv->ic_priv = ic_priv;
+
+ return 0;
+}
+
+static void prp_remove(struct imx_ic_priv *ic_priv)
+{
+ struct prp_priv *priv = ic_priv->prp_priv;
+
+ mutex_destroy(&priv->lock);
+}
+
+struct imx_ic_ops imx_ic_prp_ops = {
+ .subdev_ops = &prp_subdev_ops,
+ .internal_ops = &prp_internal_ops,
+ .entity_ops = &prp_entity_ops,
+ .init = prp_init,
+ .remove = prp_remove,
+};
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
new file mode 100644
index 000000000000..ed363fe3b3d0
--- /dev/null
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -0,0 +1,1309 @@
+/*
+ * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of video frames from the CSI or VDIC,
+ * which are routed directly to the Image Converter preprocess tasks,
+ * for resizing, colorspace conversion, and rotation.
+ *
+ * Copyright (c) 2012-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/imx.h>
+#include "imx-media.h"
+#include "imx-ic.h"
+
+/*
+ * Min/Max supported width and heights.
+ *
+ * We allow planar output, so we have to align width at the source pad
+ * by 16 pixels to meet IDMAC alignment requirements for possible planar
+ * output.
+ *
+ * TODO: move this into pad format negotiation, if capture device
+ * 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 MAX_W_SINK 4096
+#define MAX_H_SINK 4096
+#define W_ALIGN_SINK 3 /* multiple of 8 pixels */
+#define H_ALIGN_SINK 1 /* multiple of 2 lines */
+
+#define MAX_W_SRC 1024
+#define MAX_H_SRC 1024
+#define W_ALIGN_SRC 4 /* multiple of 16 pixels */
+#define H_ALIGN_SRC 1 /* multiple of 2 lines */
+
+#define S_ALIGN 1 /* multiple of 2 */
+
+struct prp_priv {
+ struct imx_media_dev *md;
+ struct imx_ic_priv *ic_priv;
+ struct media_pad pad[PRPENCVF_NUM_PADS];
+ /* the video device at output pad */
+ struct imx_media_video_dev *vdev;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ /* IPU units we require */
+ struct ipu_soc *ipu;
+ struct ipu_ic *ic;
+ struct ipuv3_channel *out_ch;
+ struct ipuv3_channel *rot_in_ch;
+ struct ipuv3_channel *rot_out_ch;
+
+ /* active vb2 buffers to send to video dev sink */
+ struct imx_media_buffer *active_vb2_buf[2];
+ struct imx_media_dma_buf underrun_buf;
+
+ int ipu_buf_num; /* ipu double buffer index: 0-1 */
+
+ /* the sink for the captured frames */
+ struct media_entity *sink;
+ /* the source subdev */
+ struct v4l2_subdev *src_sd;
+
+ struct v4l2_mbus_framefmt format_mbus[PRPENCVF_NUM_PADS];
+ const struct imx_media_pixfmt *cc[PRPENCVF_NUM_PADS];
+ struct v4l2_fract frame_interval;
+
+ struct imx_media_dma_buf rot_buf[2];
+
+ /* controls */
+ struct v4l2_ctrl_handler ctrl_hdlr;
+ int rotation; /* degrees */
+ bool hflip;
+ bool vflip;
+
+ /* derived from rotation, hflip, vflip controls */
+ enum ipu_rotate_mode rot_mode;
+
+ spinlock_t irqlock; /* protect eof_irq handler */
+
+ struct timer_list eof_timeout_timer;
+ int eof_irq;
+ int nfb4eof_irq;
+
+ int stream_count;
+ bool last_eof; /* waiting for last EOF at stream off */
+ bool nfb4eof; /* NFB4EOF encountered during streaming */
+ struct completion last_eof_comp;
+};
+
+static const struct prp_channels {
+ u32 out_ch;
+ u32 rot_in_ch;
+ u32 rot_out_ch;
+} prp_channel[] = {
+ [IC_TASK_ENCODER] = {
+ .out_ch = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+ .rot_in_ch = IPUV3_CHANNEL_MEM_ROT_ENC,
+ .rot_out_ch = IPUV3_CHANNEL_ROT_ENC_MEM,
+ },
+ [IC_TASK_VIEWFINDER] = {
+ .out_ch = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+ .rot_in_ch = IPUV3_CHANNEL_MEM_ROT_VF,
+ .rot_out_ch = IPUV3_CHANNEL_ROT_VF_MEM,
+ },
+};
+
+static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
+{
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+
+ return ic_priv->task_priv;
+}
+
+static void prp_put_ipu_resources(struct prp_priv *priv)
+{
+ if (!IS_ERR_OR_NULL(priv->ic))
+ ipu_ic_put(priv->ic);
+ priv->ic = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->out_ch))
+ ipu_idmac_put(priv->out_ch);
+ priv->out_ch = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->rot_in_ch))
+ ipu_idmac_put(priv->rot_in_ch);
+ priv->rot_in_ch = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->rot_out_ch))
+ ipu_idmac_put(priv->rot_out_ch);
+ priv->rot_out_ch = NULL;
+}
+
+static int prp_get_ipu_resources(struct prp_priv *priv)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ int ret, task = ic_priv->task_id;
+
+ priv->ipu = priv->md->ipu[ic_priv->ipu_id];
+
+ priv->ic = ipu_ic_get(priv->ipu, task);
+ if (IS_ERR(priv->ic)) {
+ v4l2_err(&ic_priv->sd, "failed to get IC\n");
+ ret = PTR_ERR(priv->ic);
+ goto out;
+ }
+
+ priv->out_ch = ipu_idmac_get(priv->ipu,
+ prp_channel[task].out_ch);
+ if (IS_ERR(priv->out_ch)) {
+ v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
+ prp_channel[task].out_ch);
+ ret = PTR_ERR(priv->out_ch);
+ goto out;
+ }
+
+ priv->rot_in_ch = ipu_idmac_get(priv->ipu,
+ prp_channel[task].rot_in_ch);
+ if (IS_ERR(priv->rot_in_ch)) {
+ v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
+ prp_channel[task].rot_in_ch);
+ ret = PTR_ERR(priv->rot_in_ch);
+ goto out;
+ }
+
+ priv->rot_out_ch = ipu_idmac_get(priv->ipu,
+ prp_channel[task].rot_out_ch);
+ if (IS_ERR(priv->rot_out_ch)) {
+ v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
+ prp_channel[task].rot_out_ch);
+ ret = PTR_ERR(priv->rot_out_ch);
+ goto out;
+ }
+
+ return 0;
+out:
+ prp_put_ipu_resources(priv);
+ return ret;
+}
+
+static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_media_buffer *done, *next;
+ struct vb2_buffer *vb;
+ dma_addr_t phys;
+
+ done = priv->active_vb2_buf[priv->ipu_buf_num];
+ if (done) {
+ vb = &done->vbuf.vb2_buf;
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, priv->nfb4eof ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ }
+
+ priv->nfb4eof = false;
+
+ /* get next queued buffer */
+ next = imx_media_capture_device_next_buf(vdev);
+ if (next) {
+ phys = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0);
+ priv->active_vb2_buf[priv->ipu_buf_num] = next;
+ } else {
+ phys = priv->underrun_buf.phys;
+ priv->active_vb2_buf[priv->ipu_buf_num] = NULL;
+ }
+
+ if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num))
+ ipu_idmac_clear_buffer(ch, priv->ipu_buf_num);
+
+ ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys);
+}
+
+static irqreturn_t prp_eof_interrupt(int irq, void *dev_id)
+{
+ struct prp_priv *priv = dev_id;
+ struct ipuv3_channel *channel;
+
+ spin_lock(&priv->irqlock);
+
+ if (priv->last_eof) {
+ complete(&priv->last_eof_comp);
+ priv->last_eof = false;
+ goto unlock;
+ }
+
+ channel = (ipu_rot_mode_is_irt(priv->rot_mode)) ?
+ priv->rot_out_ch : priv->out_ch;
+
+ prp_vb2_buf_done(priv, channel);
+
+ /* select new IPU buf */
+ ipu_idmac_select_buffer(channel, priv->ipu_buf_num);
+ /* toggle IPU double-buffer index */
+ priv->ipu_buf_num ^= 1;
+
+ /* bump the EOF timeout timer */
+ mod_timer(&priv->eof_timeout_timer,
+ jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+
+unlock:
+ spin_unlock(&priv->irqlock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t prp_nfb4eof_interrupt(int irq, void *dev_id)
+{
+ struct prp_priv *priv = dev_id;
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+
+ spin_lock(&priv->irqlock);
+
+ /*
+ * this is not an unrecoverable error, just mark
+ * the next captured frame with vb2 error flag.
+ */
+ priv->nfb4eof = true;
+
+ v4l2_err(&ic_priv->sd, "NFB4EOF\n");
+
+ spin_unlock(&priv->irqlock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+/*
+ * EOF timeout timer function. This is an unrecoverable condition
+ * without a stream restart.
+ */
+static void prp_eof_timeout(unsigned long data)
+{
+ struct prp_priv *priv = (struct prp_priv *)data;
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+
+ v4l2_err(&ic_priv->sd, "EOF timeout\n");
+
+ /* signal a fatal error to capture device */
+ imx_media_capture_device_error(vdev);
+}
+
+static void prp_setup_vb2_buf(struct prp_priv *priv, dma_addr_t *phys)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_media_buffer *buf;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ buf = imx_media_capture_device_next_buf(vdev);
+ if (buf) {
+ priv->active_vb2_buf[i] = buf;
+ phys[i] = vb2_dma_contig_plane_dma_addr(
+ &buf->vbuf.vb2_buf, 0);
+ } else {
+ priv->active_vb2_buf[i] = NULL;
+ phys[i] = priv->underrun_buf.phys;
+ }
+ }
+}
+
+static void prp_unsetup_vb2_buf(struct prp_priv *priv,
+ enum vb2_buffer_state return_status)
+{
+ struct imx_media_buffer *buf;
+ int i;
+
+ /* return any remaining active frames with return_status */
+ for (i = 0; i < 2; i++) {
+ buf = priv->active_vb2_buf[i];
+ if (buf) {
+ struct vb2_buffer *vb = &buf->vbuf.vb2_buf;
+
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, return_status);
+ }
+ }
+}
+
+static int prp_setup_channel(struct prp_priv *priv,
+ struct ipuv3_channel *channel,
+ enum ipu_rotate_mode rot_mode,
+ dma_addr_t addr0, dma_addr_t addr1,
+ bool rot_swap_width_height)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *infmt;
+ unsigned int burst_size;
+ struct ipu_image image;
+ int ret;
+
+ infmt = &priv->format_mbus[PRPENCVF_SINK_PAD];
+ outcc = vdev->cc;
+
+ ipu_cpmem_zero(channel);
+
+ memset(&image, 0, sizeof(image));
+ image.pix = vdev->fmt.fmt.pix;
+ image.rect.width = image.pix.width;
+ image.rect.height = image.pix.height;
+
+ if (rot_swap_width_height) {
+ swap(image.pix.width, image.pix.height);
+ swap(image.rect.width, image.rect.height);
+ /* recalc stride using swapped width */
+ image.pix.bytesperline = outcc->planar ?
+ image.pix.width :
+ (image.pix.width * outcc->bpp) >> 3;
+ }
+
+ image.phys0 = addr0;
+ image.phys1 = addr1;
+
+ ret = ipu_cpmem_set_image(channel, &image);
+ if (ret)
+ return ret;
+
+ if (channel == priv->rot_in_ch ||
+ channel == priv->rot_out_ch) {
+ burst_size = 8;
+ ipu_cpmem_set_block_mode(channel);
+ } else {
+ burst_size = (image.pix.width & 0xf) ? 8 : 16;
+ }
+
+ ipu_cpmem_set_burstsize(channel, burst_size);
+
+ if (rot_mode)
+ ipu_cpmem_set_rotation(channel, rot_mode);
+
+ if (image.pix.field == V4L2_FIELD_NONE &&
+ V4L2_FIELD_HAS_BOTH(infmt->field) &&
+ channel == priv->out_ch)
+ ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline);
+
+ ret = ipu_ic_task_idma_init(priv->ic, channel,
+ image.pix.width, image.pix.height,
+ burst_size, rot_mode);
+ if (ret)
+ return ret;
+
+ ipu_cpmem_set_axi_id(channel, 1);
+
+ ipu_idmac_set_double_buffer(channel, true);
+
+ return 0;
+}
+
+static int prp_setup_rotation(struct prp_priv *priv)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ const struct imx_media_pixfmt *outcc, *incc;
+ struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_pix_format *outfmt;
+ dma_addr_t phys[2];
+ int ret;
+
+ infmt = &priv->format_mbus[PRPENCVF_SINK_PAD];
+ outfmt = &vdev->fmt.fmt.pix;
+ incc = priv->cc[PRPENCVF_SINK_PAD];
+ outcc = vdev->cc;
+
+ ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[0],
+ outfmt->sizeimage);
+ if (ret) {
+ v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[0], %d\n", ret);
+ return ret;
+ }
+ ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[1],
+ outfmt->sizeimage);
+ if (ret) {
+ v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[1], %d\n", ret);
+ goto free_rot0;
+ }
+
+ ret = ipu_ic_task_init(priv->ic,
+ infmt->width, infmt->height,
+ outfmt->height, outfmt->width,
+ incc->cs, outcc->cs);
+ if (ret) {
+ v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+ goto free_rot1;
+ }
+
+ /* init the IC-PRP-->MEM IDMAC channel */
+ ret = prp_setup_channel(priv, priv->out_ch, IPU_ROTATE_NONE,
+ priv->rot_buf[0].phys, priv->rot_buf[1].phys,
+ true);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "prp_setup_channel(out_ch) failed, %d\n", ret);
+ goto free_rot1;
+ }
+
+ /* init the MEM-->IC-PRP ROT IDMAC channel */
+ ret = prp_setup_channel(priv, priv->rot_in_ch, priv->rot_mode,
+ priv->rot_buf[0].phys, priv->rot_buf[1].phys,
+ true);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "prp_setup_channel(rot_in_ch) failed, %d\n", ret);
+ goto free_rot1;
+ }
+
+ prp_setup_vb2_buf(priv, phys);
+
+ /* init the destination IC-PRP ROT-->MEM IDMAC channel */
+ ret = prp_setup_channel(priv, priv->rot_out_ch, IPU_ROTATE_NONE,
+ phys[0], phys[1],
+ false);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "prp_setup_channel(rot_out_ch) failed, %d\n", ret);
+ goto unsetup_vb2;
+ }
+
+ /* now link IC-PRP-->MEM to MEM-->IC-PRP ROT */
+ ipu_idmac_link(priv->out_ch, priv->rot_in_ch);
+
+ /* enable the IC */
+ ipu_ic_enable(priv->ic);
+
+ /* set buffers ready */
+ ipu_idmac_select_buffer(priv->out_ch, 0);
+ ipu_idmac_select_buffer(priv->out_ch, 1);
+ ipu_idmac_select_buffer(priv->rot_out_ch, 0);
+ ipu_idmac_select_buffer(priv->rot_out_ch, 1);
+
+ /* enable the channels */
+ ipu_idmac_enable_channel(priv->out_ch);
+ ipu_idmac_enable_channel(priv->rot_in_ch);
+ ipu_idmac_enable_channel(priv->rot_out_ch);
+
+ /* and finally enable the IC PRP task */
+ ipu_ic_task_enable(priv->ic);
+
+ return 0;
+
+unsetup_vb2:
+ prp_unsetup_vb2_buf(priv, VB2_BUF_STATE_QUEUED);
+free_rot1:
+ imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
+free_rot0:
+ imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
+ return ret;
+}
+
+static void prp_unsetup_rotation(struct prp_priv *priv)
+{
+ ipu_ic_task_disable(priv->ic);
+
+ ipu_idmac_disable_channel(priv->out_ch);
+ ipu_idmac_disable_channel(priv->rot_in_ch);
+ ipu_idmac_disable_channel(priv->rot_out_ch);
+
+ ipu_idmac_unlink(priv->out_ch, priv->rot_in_ch);
+
+ ipu_ic_disable(priv->ic);
+
+ imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
+ imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
+}
+
+static int prp_setup_norotation(struct prp_priv *priv)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ const struct imx_media_pixfmt *outcc, *incc;
+ struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_pix_format *outfmt;
+ dma_addr_t phys[2];
+ int ret;
+
+ infmt = &priv->format_mbus[PRPENCVF_SINK_PAD];
+ outfmt = &vdev->fmt.fmt.pix;
+ incc = priv->cc[PRPENCVF_SINK_PAD];
+ outcc = vdev->cc;
+
+ ret = ipu_ic_task_init(priv->ic,
+ infmt->width, infmt->height,
+ outfmt->width, outfmt->height,
+ incc->cs, outcc->cs);
+ if (ret) {
+ v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+ return ret;
+ }
+
+ prp_setup_vb2_buf(priv, phys);
+
+ /* init the IC PRP-->MEM IDMAC channel */
+ ret = prp_setup_channel(priv, priv->out_ch, priv->rot_mode,
+ phys[0], phys[1], false);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "prp_setup_channel(out_ch) failed, %d\n", ret);
+ goto unsetup_vb2;
+ }
+
+ ipu_cpmem_dump(priv->out_ch);
+ ipu_ic_dump(priv->ic);
+ ipu_dump(priv->ipu);
+
+ ipu_ic_enable(priv->ic);
+
+ /* set buffers ready */
+ ipu_idmac_select_buffer(priv->out_ch, 0);
+ ipu_idmac_select_buffer(priv->out_ch, 1);
+
+ /* enable the channels */
+ ipu_idmac_enable_channel(priv->out_ch);
+
+ /* enable the IC task */
+ ipu_ic_task_enable(priv->ic);
+
+ return 0;
+
+unsetup_vb2:
+ prp_unsetup_vb2_buf(priv, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void prp_unsetup_norotation(struct prp_priv *priv)
+{
+ ipu_ic_task_disable(priv->ic);
+ ipu_idmac_disable_channel(priv->out_ch);
+ ipu_ic_disable(priv->ic);
+}
+
+static void prp_unsetup(struct prp_priv *priv,
+ enum vb2_buffer_state state)
+{
+ if (ipu_rot_mode_is_irt(priv->rot_mode))
+ prp_unsetup_rotation(priv);
+ else
+ prp_unsetup_norotation(priv);
+
+ prp_unsetup_vb2_buf(priv, state);
+}
+
+static int prp_start(struct prp_priv *priv)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct v4l2_pix_format *outfmt;
+ int ret;
+
+ ret = prp_get_ipu_resources(priv);
+ if (ret)
+ return ret;
+
+ outfmt = &vdev->fmt.fmt.pix;
+
+ ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf,
+ outfmt->sizeimage);
+ if (ret)
+ goto out_put_ipu;
+
+ priv->ipu_buf_num = 0;
+
+ /* init EOF completion waitq */
+ init_completion(&priv->last_eof_comp);
+ priv->last_eof = false;
+ priv->nfb4eof = false;
+
+ if (ipu_rot_mode_is_irt(priv->rot_mode))
+ ret = prp_setup_rotation(priv);
+ else
+ ret = prp_setup_norotation(priv);
+ if (ret)
+ goto out_free_underrun;
+
+ priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+ priv->out_ch,
+ IPU_IRQ_NFB4EOF);
+ ret = devm_request_irq(ic_priv->dev, priv->nfb4eof_irq,
+ prp_nfb4eof_interrupt, 0,
+ "imx-ic-prp-nfb4eof", priv);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "Error registering NFB4EOF irq: %d\n", ret);
+ goto out_unsetup;
+ }
+
+ if (ipu_rot_mode_is_irt(priv->rot_mode))
+ priv->eof_irq = ipu_idmac_channel_irq(
+ priv->ipu, priv->rot_out_ch, IPU_IRQ_EOF);
+ else
+ priv->eof_irq = ipu_idmac_channel_irq(
+ priv->ipu, priv->out_ch, IPU_IRQ_EOF);
+
+ ret = devm_request_irq(ic_priv->dev, priv->eof_irq,
+ prp_eof_interrupt, 0,
+ "imx-ic-prp-eof", priv);
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "Error registering eof irq: %d\n", ret);
+ goto out_free_nfb4eof_irq;
+ }
+
+ /* start the EOF timeout timer */
+ mod_timer(&priv->eof_timeout_timer,
+ jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+
+ return 0;
+
+out_free_nfb4eof_irq:
+ devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
+out_unsetup:
+ prp_unsetup(priv, VB2_BUF_STATE_QUEUED);
+out_free_underrun:
+ imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+out_put_ipu:
+ prp_put_ipu_resources(priv);
+ return ret;
+}
+
+static void prp_stop(struct prp_priv *priv)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ unsigned long flags;
+ int ret;
+
+ /* mark next EOF interrupt as the last before stream off */
+ spin_lock_irqsave(&priv->irqlock, flags);
+ priv->last_eof = true;
+ spin_unlock_irqrestore(&priv->irqlock, flags);
+
+ /*
+ * and then wait for interrupt handler to mark completion.
+ */
+ ret = wait_for_completion_timeout(
+ &priv->last_eof_comp,
+ msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+ if (ret == 0)
+ v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n");
+
+ devm_free_irq(ic_priv->dev, priv->eof_irq, priv);
+ devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
+
+ prp_unsetup(priv, VB2_BUF_STATE_ERROR);
+
+ imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+
+ /* cancel the EOF timeout timer */
+ del_timer_sync(&priv->eof_timeout_timer);
+
+ prp_put_ipu_resources(priv);
+}
+
+static struct v4l2_mbus_framefmt *
+__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ic_priv->sd, cfg, pad);
+ else
+ return &priv->format_mbus[pad];
+}
+
+/*
+ * Applies IC resizer and IDMAC alignment restrictions to output
+ * rectangle given the input rectangle, and depending on given
+ * rotation mode.
+ *
+ * The IC resizer cannot downsize more than 4:1. Note also that
+ * for 90 or 270 rotation, _both_ output width and height must
+ * be aligned by W_ALIGN_SRC, because the intermediate rotation
+ * buffer swaps output width/height, and the final output buffer
+ * does not.
+ *
+ * Returns true if the output rectangle was modified.
+ */
+static bool prp_bound_align_output(struct v4l2_mbus_framefmt *outfmt,
+ struct v4l2_mbus_framefmt *infmt,
+ enum ipu_rotate_mode rot_mode)
+{
+ u32 orig_width = outfmt->width;
+ u32 orig_height = outfmt->height;
+
+ if (ipu_rot_mode_is_irt(rot_mode))
+ v4l_bound_align_image(&outfmt->width,
+ infmt->height / 4, MAX_H_SRC,
+ W_ALIGN_SRC,
+ &outfmt->height,
+ infmt->width / 4, MAX_W_SRC,
+ W_ALIGN_SRC, S_ALIGN);
+ else
+ v4l_bound_align_image(&outfmt->width,
+ infmt->width / 4, MAX_W_SRC,
+ W_ALIGN_SRC,
+ &outfmt->height,
+ infmt->height / 4, MAX_H_SRC,
+ H_ALIGN_SRC, S_ALIGN);
+
+ return outfmt->width != orig_width || outfmt->height != orig_height;
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int prp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad >= PRPENCVF_NUM_PADS)
+ return -EINVAL;
+
+ return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_ANY);
+}
+
+static int prp_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= PRPENCVF_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdformat->format = *fmt;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static void prp_try_fmt(struct prp_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat,
+ const struct imx_media_pixfmt **cc)
+{
+ struct v4l2_mbus_framefmt *infmt;
+
+ *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_ANY);
+ if (!*cc) {
+ u32 code;
+
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
+ *cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which);
+
+ if (sdformat->pad == PRPENCVF_SRC_PAD) {
+ if (sdformat->format.field != V4L2_FIELD_NONE)
+ sdformat->format.field = infmt->field;
+
+ prp_bound_align_output(&sdformat->format, infmt,
+ priv->rot_mode);
+
+ /* propagate colorimetry from sink */
+ sdformat->format.colorspace = infmt->colorspace;
+ sdformat->format.xfer_func = infmt->xfer_func;
+ sdformat->format.quantization = infmt->quantization;
+ sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
+ } else {
+ v4l_bound_align_image(&sdformat->format.width,
+ MIN_W_SINK, MAX_W_SINK, W_ALIGN_SINK,
+ &sdformat->format.height,
+ MIN_H_SINK, MAX_H_SINK, H_ALIGN_SINK,
+ S_ALIGN);
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
+ }
+}
+
+static int prp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct imx_media_video_dev *vdev = priv->vdev;
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_pix_format vdev_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= PRPENCVF_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ prp_try_fmt(priv, cfg, sdformat, &cc);
+
+ fmt = __prp_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ *fmt = sdformat->format;
+
+ /* propagate a default format to source pad */
+ if (sdformat->pad == PRPENCVF_SINK_PAD) {
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *outfmt;
+ struct v4l2_subdev_format format;
+
+ format.pad = PRPENCVF_SRC_PAD;
+ format.which = sdformat->which;
+ format.format = sdformat->format;
+ prp_try_fmt(priv, cfg, &format, &outcc);
+
+ outfmt = __prp_get_fmt(priv, cfg, PRPENCVF_SRC_PAD,
+ sdformat->which);
+ *outfmt = format.format;
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[PRPENCVF_SRC_PAD] = outcc;
+ }
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ goto out;
+
+ priv->cc[sdformat->pad] = cc;
+
+ /* propagate output pad format to capture device */
+ imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt,
+ &priv->format_mbus[PRPENCVF_SRC_PAD],
+ priv->cc[PRPENCVF_SRC_PAD]);
+ mutex_unlock(&priv->lock);
+ imx_media_capture_device_set_format(vdev, &vdev_fmt);
+
+ return 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_subdev_format format = {0};
+ const struct imx_media_pixfmt *cc;
+ int ret = 0;
+
+ if (fse->pad >= PRPENCVF_NUM_PADS || fse->index != 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ format.pad = fse->pad;
+ format.which = fse->which;
+ format.format.code = fse->code;
+ format.format.width = 1;
+ format.format.height = 1;
+ prp_try_fmt(priv, cfg, &format, &cc);
+ fse->min_width = format.format.width;
+ fse->min_height = format.format.height;
+
+ if (format.format.code != fse->code) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ format.format.code = fse->code;
+ format.format.width = -1;
+ format.format.height = -1;
+ prp_try_fmt(priv, cfg, &format, &cc);
+ fse->max_width = format.format.width;
+ fse->max_height = format.format.height;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+ struct prp_priv *priv = ic_priv->task_priv;
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ mutex_lock(&priv->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SINK) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->src_sd = remote_sd;
+ } else {
+ priv->src_sd = NULL;
+ }
+
+ goto out;
+ }
+
+ /* this is the source pad */
+
+ /* the remote must be the device node */
+ if (!is_media_entity_v4l2_video_device(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->sink) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ priv->sink = NULL;
+ goto out;
+ }
+
+ priv->sink = remote->entity;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct prp_priv *priv = container_of(ctrl->handler,
+ struct prp_priv, ctrl_hdlr);
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ enum ipu_rotate_mode rot_mode;
+ int rotation, ret = 0;
+ bool hflip, vflip;
+
+ mutex_lock(&priv->lock);
+
+ rotation = priv->rotation;
+ hflip = priv->hflip;
+ vflip = priv->vflip;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ hflip = (ctrl->val == 1);
+ break;
+ case V4L2_CID_VFLIP:
+ vflip = (ctrl->val == 1);
+ break;
+ case V4L2_CID_ROTATE:
+ rotation = ctrl->val;
+ break;
+ default:
+ v4l2_err(&ic_priv->sd, "Invalid control\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
+ if (ret)
+ goto out;
+
+ if (rot_mode != priv->rot_mode) {
+ struct v4l2_mbus_framefmt outfmt, infmt;
+
+ /* can't change rotation mid-streaming */
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ outfmt = priv->format_mbus[PRPENCVF_SRC_PAD];
+ infmt = priv->format_mbus[PRPENCVF_SINK_PAD];
+
+ if (prp_bound_align_output(&outfmt, &infmt, rot_mode)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->rot_mode = rot_mode;
+ priv->rotation = rotation;
+ priv->hflip = hflip;
+ priv->vflip = vflip;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops prp_ctrl_ops = {
+ .s_ctrl = prp_s_ctrl,
+};
+
+static int prp_init_controls(struct prp_priv *priv)
+{
+ struct imx_ic_priv *ic_priv = priv->ic_priv;
+ struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdlr, 3);
+
+ v4l2_ctrl_new_std(hdlr, &prp_ctrl_ops, V4L2_CID_HFLIP,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdlr, &prp_ctrl_ops, V4L2_CID_VFLIP,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdlr, &prp_ctrl_ops, V4L2_CID_ROTATE,
+ 0, 270, 90, 0);
+
+ ic_priv->sd.ctrl_handler = hdlr;
+
+ if (hdlr->error) {
+ ret = hdlr->error;
+ goto out_free;
+ }
+
+ v4l2_ctrl_handler_setup(hdlr);
+ return 0;
+
+out_free:
+ v4l2_ctrl_handler_free(hdlr);
+ return ret;
+}
+
+static int prp_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
+ struct prp_priv *priv = ic_priv->task_priv;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->src_sd || !priv->sink) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (priv->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(ic_priv->dev, "stream %s\n", enable ? "ON" : "OFF");
+
+ if (enable)
+ ret = prp_start(priv);
+ else
+ prp_stop(priv);
+ if (ret)
+ goto out;
+
+ /* start/stop upstream */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret) {
+ if (enable)
+ prp_stop(priv);
+ goto out;
+ }
+
+update_count:
+ priv->stream_count += enable ? 1 : -1;
+ if (priv->stream_count < 0)
+ priv->stream_count = 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int prp_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+
+ if (fi->pad >= PRPENCVF_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+ fi->interval = priv->frame_interval;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int prp_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+
+ if (fi->pad >= PRPENCVF_NUM_PADS)
+ return -EINVAL;
+
+ /* No limits on frame interval */
+ mutex_lock(&priv->lock);
+ priv->frame_interval = fi->interval;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int prp_registered(struct v4l2_subdev *sd)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+ int i, ret;
+ u32 code;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ for (i = 0; i < PRPENCVF_NUM_PADS; i++) {
+ priv->pad[i].flags = (i == PRPENCVF_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ /* set a default mbus format */
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
+ 640, 480, code, V4L2_FIELD_NONE,
+ &priv->cc[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* init default frame interval */
+ priv->frame_interval.numerator = 1;
+ priv->frame_interval.denominator = 30;
+
+ ret = media_entity_pads_init(&sd->entity, PRPENCVF_NUM_PADS,
+ priv->pad);
+ if (ret)
+ return ret;
+
+ ret = imx_media_capture_device_register(priv->vdev);
+ if (ret)
+ return ret;
+
+ ret = imx_media_add_video_device(priv->md, priv->vdev);
+ if (ret)
+ goto unreg;
+
+ ret = prp_init_controls(priv);
+ if (ret)
+ goto unreg;
+
+ return 0;
+unreg:
+ imx_media_capture_device_unregister(priv->vdev);
+ return ret;
+}
+
+static void prp_unregistered(struct v4l2_subdev *sd)
+{
+ struct prp_priv *priv = sd_to_priv(sd);
+
+ imx_media_capture_device_unregister(priv->vdev);
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+}
+
+static const struct v4l2_subdev_pad_ops prp_pad_ops = {
+ .enum_mbus_code = prp_enum_mbus_code,
+ .enum_frame_size = prp_enum_frame_size,
+ .get_fmt = prp_get_fmt,
+ .set_fmt = prp_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops prp_video_ops = {
+ .g_frame_interval = prp_g_frame_interval,
+ .s_frame_interval = prp_s_frame_interval,
+ .s_stream = prp_s_stream,
+};
+
+static const struct media_entity_operations prp_entity_ops = {
+ .link_setup = prp_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_ops prp_subdev_ops = {
+ .video = &prp_video_ops,
+ .pad = &prp_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops prp_internal_ops = {
+ .registered = prp_registered,
+ .unregistered = prp_unregistered,
+};
+
+static int prp_init(struct imx_ic_priv *ic_priv)
+{
+ struct prp_priv *priv;
+
+ priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ic_priv->task_priv = priv;
+ priv->ic_priv = ic_priv;
+
+ spin_lock_init(&priv->irqlock);
+ init_timer(&priv->eof_timeout_timer);
+ priv->eof_timeout_timer.data = (unsigned long)priv;
+ priv->eof_timeout_timer.function = prp_eof_timeout;
+
+ priv->vdev = imx_media_capture_device_init(&ic_priv->sd,
+ PRPENCVF_SRC_PAD);
+ if (IS_ERR(priv->vdev))
+ return PTR_ERR(priv->vdev);
+
+ mutex_init(&priv->lock);
+
+ return 0;
+}
+
+static void prp_remove(struct imx_ic_priv *ic_priv)
+{
+ struct prp_priv *priv = ic_priv->task_priv;
+
+ mutex_destroy(&priv->lock);
+ imx_media_capture_device_remove(priv->vdev);
+}
+
+struct imx_ic_ops imx_ic_prpencvf_ops = {
+ .subdev_ops = &prp_subdev_ops,
+ .internal_ops = &prp_internal_ops,
+ .entity_ops = &prp_entity_ops,
+ .init = prp_init,
+ .remove = prp_remove,
+};
diff --git a/drivers/staging/media/imx/imx-ic.h b/drivers/staging/media/imx/imx-ic.h
new file mode 100644
index 000000000000..6b2267bda8ab
--- /dev/null
+++ b/drivers/staging/media/imx/imx-ic.h
@@ -0,0 +1,38 @@
+/*
+ * V4L2 Image Converter Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_IC_H
+#define _IMX_IC_H
+
+#include <media/v4l2-subdev.h>
+
+struct imx_ic_priv {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ int ipu_id;
+ int task_id;
+ void *prp_priv;
+ void *task_priv;
+};
+
+struct imx_ic_ops {
+ const struct v4l2_subdev_ops *subdev_ops;
+ const struct v4l2_subdev_internal_ops *internal_ops;
+ const struct media_entity_operations *entity_ops;
+
+ int (*init)(struct imx_ic_priv *ic_priv);
+ void (*remove)(struct imx_ic_priv *ic_priv);
+};
+
+extern struct imx_ic_ops imx_ic_prp_ops;
+extern struct imx_ic_ops imx_ic_prpencvf_ops;
+extern struct imx_ic_ops imx_ic_pp_ops;
+
+#endif
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
new file mode 100644
index 000000000000..ddab4c249da2
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -0,0 +1,775 @@
+/*
+ * Video Capture Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+struct capture_priv {
+ struct imx_media_video_dev vdev;
+
+ struct v4l2_subdev *src_sd;
+ int src_sd_pad;
+ struct device *dev;
+
+ struct imx_media_dev *md;
+
+ struct media_pad vdev_pad;
+
+ struct mutex mutex; /* capture device mutex */
+
+ /* the videobuf2 queue */
+ struct vb2_queue q;
+ /* list of ready imx_media_buffer's from q */
+ struct list_head ready_q;
+ /* protect ready_q */
+ spinlock_t q_lock;
+
+ /* controls inherited from subdevs */
+ struct v4l2_ctrl_handler ctrl_hdlr;
+
+ /* misc status */
+ bool stop; /* streaming is stopping */
+};
+
+#define to_capture_priv(v) container_of(v, struct capture_priv, vdev)
+
+/* In bytes, per queue */
+#define VID_MEM_LIMIT SZ_64M
+
+static struct vb2_ops capture_qops;
+
+/*
+ * Video ioctls follow
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ strncpy(cap->driver, "imx-media-capture", sizeof(cap->driver) - 1);
+ strncpy(cap->card, "imx-media-capture", sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", priv->src_sd->name);
+
+ return 0;
+}
+
+static int capture_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .index = fsize->index,
+ .pad = priv->src_sd_pad,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ cc = imx_media_find_format(fsize->pixel_format, CS_SEL_ANY, true);
+ if (!cc)
+ return -EINVAL;
+
+ fse.code = cc->codes[0];
+
+ ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ 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;
+ } else {
+ 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;
+}
+
+static int capture_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .pad = priv->src_sd_pad,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ cc = imx_media_find_format(fival->pixel_format, CS_SEL_ANY, true);
+ if (!cc)
+ return -EINVAL;
+
+ fie.code = cc->codes[0];
+
+ ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc_src;
+ struct v4l2_subdev_format fmt_src;
+ u32 fourcc;
+ int ret;
+
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret) {
+ v4l2_err(priv->src_sd, "failed to get src_sd format\n");
+ return ret;
+ }
+
+ cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ if (!cc_src)
+ cc_src = imx_media_find_mbus_format(fmt_src.format.code,
+ CS_SEL_ANY, true);
+ if (!cc_src)
+ return -EINVAL;
+
+ if (cc_src->bayer) {
+ if (f->index != 0)
+ return -EINVAL;
+ fourcc = cc_src->fourcc;
+ } else {
+ u32 cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+
+ ret = imx_media_enum_format(&fourcc, f->index, cs_sel);
+ if (ret)
+ return ret;
+ }
+
+ f->pixelformat = fourcc;
+
+ return 0;
+}
+
+static int capture_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ *f = priv->vdev.fmt;
+
+ return 0;
+}
+
+static int capture_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_format fmt_src;
+ const struct imx_media_pixfmt *cc, *cc_src;
+ int ret;
+
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret)
+ return ret;
+
+ cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ if (!cc_src)
+ cc_src = imx_media_find_mbus_format(fmt_src.format.code,
+ CS_SEL_ANY, true);
+ if (!cc_src)
+ return -EINVAL;
+
+ if (cc_src->bayer) {
+ cc = cc_src;
+ } else {
+ u32 fourcc, cs_sel;
+
+ cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+ fourcc = f->fmt.pix.pixelformat;
+
+ cc = imx_media_find_format(fourcc, cs_sel, false);
+ if (!cc) {
+ imx_media_enum_format(&fourcc, 0, cs_sel);
+ cc = imx_media_find_format(fourcc, cs_sel, false);
+ }
+ }
+
+ imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc);
+
+ return 0;
+}
+
+static int capture_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&priv->q)) {
+ v4l2_err(priv->src_sd, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = capture_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ priv->vdev.fmt.fmt.pix = f->fmt.pix;
+ priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat,
+ CS_SEL_ANY, true);
+
+ return 0;
+}
+
+static int capture_querystd(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ return v4l2_subdev_call(priv->src_sd, video, querystd, std);
+}
+
+static int capture_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ return v4l2_subdev_call(priv->src_sd, video, g_std, std);
+}
+
+static int capture_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ if (vb2_is_busy(&priv->q))
+ return -EBUSY;
+
+ return v4l2_subdev_call(priv->src_sd, video, s_std, std);
+}
+
+static int capture_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_frame_interval fi;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.pad = priv->src_sd_pad;
+ ret = v4l2_subdev_call(priv->src_sd, video, g_frame_interval, &fi);
+ if (ret < 0)
+ return ret;
+
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.capture.timeperframe = fi.interval;
+
+ return 0;
+}
+
+static int capture_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_frame_interval fi;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.pad = priv->src_sd_pad;
+ fi.interval = a->parm.capture.timeperframe;
+ ret = v4l2_subdev_call(priv->src_sd, video, s_frame_interval, &fi);
+ if (ret < 0)
+ return ret;
+
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.capture.timeperframe = fi.interval;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops capture_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_framesizes = capture_enum_framesizes,
+ .vidioc_enum_frameintervals = capture_enum_frameintervals,
+
+ .vidioc_enum_fmt_vid_cap = capture_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = capture_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = capture_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = capture_s_fmt_vid_cap,
+
+ .vidioc_querystd = capture_querystd,
+ .vidioc_g_std = capture_g_std,
+ .vidioc_s_std = capture_s_std,
+
+ .vidioc_g_parm = capture_g_parm,
+ .vidioc_s_parm = capture_s_parm,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int capture_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix = &priv->vdev.fmt.fmt.pix;
+ unsigned int count = *nbuffers;
+
+ if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (*nplanes) {
+ if (*nplanes != 1 || sizes[0] < pix->sizeimage)
+ return -EINVAL;
+ count += vq->num_buffers;
+ }
+
+ count = min_t(__u32, VID_MEM_LIMIT / pix->sizeimage, count);
+
+ if (*nplanes)
+ *nbuffers = (count < vq->num_buffers) ? 0 :
+ count - vq->num_buffers;
+ else
+ *nbuffers = count;
+
+ *nplanes = 1;
+ sizes[0] = pix->sizeimage;
+
+ return 0;
+}
+
+static int capture_buf_init(struct vb2_buffer *vb)
+{
+ struct imx_media_buffer *buf = to_imx_media_vb(vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int capture_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix = &priv->vdev.fmt.fmt.pix;
+
+ if (vb2_plane_size(vb, 0) < pix->sizeimage) {
+ v4l2_err(priv->src_sd,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), (long)pix->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, pix->sizeimage);
+
+ return 0;
+}
+
+static void capture_buf_queue(struct vb2_buffer *vb)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vb->vb2_queue);
+ struct imx_media_buffer *buf = to_imx_media_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+
+ list_add_tail(&buf->list, &priv->ready_q);
+
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+
+static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct imx_media_buffer *buf, *tmp;
+ unsigned long flags;
+ int ret;
+
+ if (vb2_is_streaming(vq))
+ return 0;
+
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
+ true);
+ if (ret) {
+ v4l2_err(priv->src_sd, "pipeline start failed with %d\n", ret);
+ goto return_bufs;
+ }
+
+ priv->stop = false;
+
+ return 0;
+
+return_bufs:
+ spin_lock_irqsave(&priv->q_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &priv->ready_q, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+ return ret;
+}
+
+static void capture_stop_streaming(struct vb2_queue *vq)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct imx_media_buffer *frame;
+ unsigned long flags;
+ int ret;
+
+ if (!vb2_is_streaming(vq))
+ return;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+ priv->stop = true;
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
+ false);
+ if (ret)
+ v4l2_warn(priv->src_sd, "pipeline stop failed with %d\n", ret);
+
+ /* release all active buffers */
+ spin_lock_irqsave(&priv->q_lock, flags);
+ while (!list_empty(&priv->ready_q)) {
+ frame = list_entry(priv->ready_q.next,
+ struct imx_media_buffer, list);
+ list_del(&frame->list);
+ vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+
+static struct vb2_ops capture_qops = {
+ .queue_setup = capture_queue_setup,
+ .buf_init = capture_buf_init,
+ .buf_prepare = capture_buf_prepare,
+ .buf_queue = capture_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = capture_start_streaming,
+ .stop_streaming = capture_stop_streaming,
+};
+
+/*
+ * File operations
+ */
+static int capture_open(struct file *file)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct video_device *vfd = priv->vdev.vfd;
+ int ret;
+
+ if (mutex_lock_interruptible(&priv->mutex))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ v4l2_err(priv->src_sd, "v4l2_fh_open failed\n");
+
+ ret = v4l2_pipeline_pm_use(&vfd->entity, 1);
+ if (ret)
+ v4l2_fh_release(file);
+
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int capture_release(struct file *file)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct video_device *vfd = priv->vdev.vfd;
+ struct vb2_queue *vq = &priv->q;
+ int ret = 0;
+
+ mutex_lock(&priv->mutex);
+
+ if (file->private_data == vq->owner) {
+ vb2_queue_release(vq);
+ vq->owner = NULL;
+ }
+
+ v4l2_pipeline_pm_use(&vfd->entity, 0);
+
+ v4l2_fh_release(file);
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static const struct v4l2_file_operations capture_fops = {
+ .owner = THIS_MODULE,
+ .open = capture_open,
+ .release = capture_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static struct video_device capture_videodev = {
+ .fops = &capture_fops,
+ .ioctl_ops = &capture_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_RX,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
+};
+
+void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
+ struct v4l2_pix_format *pix)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+
+ mutex_lock(&priv->mutex);
+ priv->vdev.fmt.fmt.pix = *pix;
+ priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY,
+ true);
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format);
+
+struct imx_media_buffer *
+imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct imx_media_buffer *buf = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+
+ /* get next queued buffer */
+ if (!list_empty(&priv->ready_q)) {
+ buf = list_entry(priv->ready_q.next, struct imx_media_buffer,
+ list);
+ list_del(&buf->list);
+ }
+
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_next_buf);
+
+void imx_media_capture_device_error(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct vb2_queue *vq = &priv->q;
+ unsigned long flags;
+
+ if (!vb2_is_streaming(vq))
+ return;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+ vb2_queue_error(vq);
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_error);
+
+int imx_media_capture_device_register(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct v4l2_subdev *sd = priv->src_sd;
+ struct video_device *vfd = vdev->vfd;
+ struct vb2_queue *vq = &priv->q;
+ struct v4l2_subdev_format fmt_src;
+ int ret;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ vfd->v4l2_dev = sd->v4l2_dev;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(sd, "Failed to register video device\n");
+ return ret;
+ }
+
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ vq->drv_priv = priv;
+ vq->buf_struct_size = sizeof(struct imx_media_buffer);
+ vq->ops = &capture_qops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vq->lock = &priv->mutex;
+ vq->min_buffers_needed = 2;
+ vq->dev = priv->dev;
+
+ ret = vb2_queue_init(vq);
+ if (ret) {
+ v4l2_err(sd, "vb2_queue_init failed\n");
+ goto unreg;
+ }
+
+ INIT_LIST_HEAD(&priv->ready_q);
+
+ priv->vdev_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vfd->entity, 1, &priv->vdev_pad);
+ if (ret) {
+ v4l2_err(sd, "failed to init dev pad\n");
+ goto unreg;
+ }
+
+ /* create the link from the src_sd devnode pad to device node */
+ ret = media_create_pad_link(&sd->entity, priv->src_sd_pad,
+ &vfd->entity, 0, 0);
+ if (ret) {
+ v4l2_err(sd, "failed to create link to device node\n");
+ goto unreg;
+ }
+
+ /* setup default format */
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret) {
+ v4l2_err(sd, "failed to get src_sd format\n");
+ goto unreg;
+ }
+
+ vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix,
+ &fmt_src.format, NULL);
+ vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat,
+ CS_SEL_ANY, false);
+
+ v4l2_info(sd, "Registered %s as /dev/%s\n", vfd->name,
+ video_device_node_name(vfd));
+
+ vfd->ctrl_handler = &priv->ctrl_hdlr;
+
+ return 0;
+unreg:
+ video_unregister_device(vfd);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_register);
+
+void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct video_device *vfd = priv->vdev.vfd;
+
+ mutex_lock(&priv->mutex);
+
+ if (video_is_registered(vfd)) {
+ video_unregister_device(vfd);
+ media_entity_cleanup(&vfd->entity);
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_unregister);
+
+struct imx_media_video_dev *
+imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad)
+{
+ struct capture_priv *priv;
+ struct video_device *vfd;
+
+ priv = devm_kzalloc(src_sd->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->src_sd = src_sd;
+ priv->src_sd_pad = pad;
+ priv->dev = src_sd->dev;
+
+ mutex_init(&priv->mutex);
+ spin_lock_init(&priv->q_lock);
+
+ snprintf(capture_videodev.name, sizeof(capture_videodev.name),
+ "%s capture", src_sd->name);
+
+ vfd = video_device_alloc();
+ if (!vfd)
+ return ERR_PTR(-ENOMEM);
+
+ *vfd = capture_videodev;
+ vfd->lock = &priv->mutex;
+ vfd->queue = &priv->q;
+ priv->vdev.vfd = vfd;
+
+ video_set_drvdata(vfd, priv);
+
+ v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
+
+ return &priv->vdev;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_init);
+
+void imx_media_capture_device_remove(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_remove);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 video capture interface driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
new file mode 100644
index 000000000000..a2d26693912e
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -0,0 +1,1817 @@
+/*
+ * V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2017 Mentor Graphics Inc.
+ * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+/*
+ * Min/Max supported width and heights.
+ *
+ * We allow planar output, so we have to align width by 16 pixels
+ * to meet IDMAC alignment requirements.
+ *
+ * TODO: move this into pad format negotiation, if capture device
+ * has not requested planar formats, we should allow 8 pixel
+ * alignment.
+ */
+#define MIN_W 176
+#define MIN_H 144
+#define MAX_W 4096
+#define MAX_H 4096
+#define W_ALIGN 4 /* multiple of 16 pixels */
+#define H_ALIGN 1 /* multiple of 2 lines */
+#define S_ALIGN 1 /* multiple of 2 */
+
+/*
+ * struct csi_skip_desc - CSI frame skipping descriptor
+ * @keep - number of frames kept per max_ratio frames
+ * @max_ratio - width of skip_smfc, written to MAX_RATIO bitfield
+ * @skip_smfc - skip pattern written to the SKIP_SMFC bitfield
+ */
+struct csi_skip_desc {
+ u8 keep;
+ u8 max_ratio;
+ u8 skip_smfc;
+};
+
+struct csi_priv {
+ struct device *dev;
+ struct ipu_soc *ipu;
+ struct imx_media_dev *md;
+ struct v4l2_subdev sd;
+ struct media_pad pad[CSI_NUM_PADS];
+ /* the video device at IDMAC output pad */
+ struct imx_media_video_dev *vdev;
+ struct imx_media_fim *fim;
+ int csi_id;
+ int smfc_id;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ int active_output_pad;
+
+ struct ipuv3_channel *idmac_ch;
+ struct ipu_smfc *smfc;
+ struct ipu_csi *csi;
+
+ struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
+ const struct imx_media_pixfmt *cc[CSI_NUM_PADS];
+ struct v4l2_fract frame_interval[CSI_NUM_PADS];
+ struct v4l2_rect crop;
+ struct v4l2_rect compose;
+ const struct csi_skip_desc *skip;
+
+ /* active vb2 buffers to send to video dev sink */
+ struct imx_media_buffer *active_vb2_buf[2];
+ struct imx_media_dma_buf underrun_buf;
+
+ int ipu_buf_num; /* ipu double buffer index: 0-1 */
+
+ /* the sink for the captured frames */
+ struct media_entity *sink;
+ enum ipu_csi_dest dest;
+ /* the source subdev */
+ struct v4l2_subdev *src_sd;
+
+ /* the mipi virtual channel number at link validate */
+ int vc_num;
+
+ /* the attached sensor at stream on */
+ struct imx_media_subdev *sensor;
+
+ spinlock_t irqlock; /* protect eof_irq handler */
+ struct timer_list eof_timeout_timer;
+ int eof_irq;
+ int nfb4eof_irq;
+
+ struct v4l2_ctrl_handler ctrl_hdlr;
+
+ int stream_count; /* streaming counter */
+ bool last_eof; /* waiting for last EOF at stream off */
+ bool nfb4eof; /* NFB4EOF encountered during streaming */
+ struct completion last_eof_comp;
+};
+
+static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_priv, sd);
+}
+
+static void csi_idmac_put_ipu_resources(struct csi_priv *priv)
+{
+ if (!IS_ERR_OR_NULL(priv->idmac_ch))
+ ipu_idmac_put(priv->idmac_ch);
+ priv->idmac_ch = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->smfc))
+ ipu_smfc_put(priv->smfc);
+ priv->smfc = NULL;
+}
+
+static int csi_idmac_get_ipu_resources(struct csi_priv *priv)
+{
+ int ch_num, ret;
+
+ ch_num = IPUV3_CHANNEL_CSI0 + priv->smfc_id;
+
+ priv->smfc = ipu_smfc_get(priv->ipu, ch_num);
+ if (IS_ERR(priv->smfc)) {
+ v4l2_err(&priv->sd, "failed to get SMFC\n");
+ ret = PTR_ERR(priv->smfc);
+ goto out;
+ }
+
+ priv->idmac_ch = ipu_idmac_get(priv->ipu, ch_num);
+ if (IS_ERR(priv->idmac_ch)) {
+ v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+ ch_num);
+ ret = PTR_ERR(priv->idmac_ch);
+ goto out;
+ }
+
+ return 0;
+out:
+ csi_idmac_put_ipu_resources(priv);
+ return ret;
+}
+
+static void csi_vb2_buf_done(struct csi_priv *priv)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_media_buffer *done, *next;
+ struct vb2_buffer *vb;
+ dma_addr_t phys;
+
+ done = priv->active_vb2_buf[priv->ipu_buf_num];
+ if (done) {
+ vb = &done->vbuf.vb2_buf;
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, priv->nfb4eof ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ }
+
+ priv->nfb4eof = false;
+
+ /* get next queued buffer */
+ next = imx_media_capture_device_next_buf(vdev);
+ if (next) {
+ phys = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0);
+ priv->active_vb2_buf[priv->ipu_buf_num] = next;
+ } else {
+ phys = priv->underrun_buf.phys;
+ priv->active_vb2_buf[priv->ipu_buf_num] = NULL;
+ }
+
+ if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num))
+ ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num);
+
+ ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys);
+}
+
+static irqreturn_t csi_idmac_eof_interrupt(int irq, void *dev_id)
+{
+ struct csi_priv *priv = dev_id;
+
+ spin_lock(&priv->irqlock);
+
+ if (priv->last_eof) {
+ complete(&priv->last_eof_comp);
+ priv->last_eof = false;
+ goto unlock;
+ }
+
+ if (priv->fim) {
+ struct timespec cur_ts;
+
+ ktime_get_ts(&cur_ts);
+ /* call frame interval monitor */
+ imx_media_fim_eof_monitor(priv->fim, &cur_ts);
+ }
+
+ csi_vb2_buf_done(priv);
+
+ /* select new IPU buf */
+ ipu_idmac_select_buffer(priv->idmac_ch, priv->ipu_buf_num);
+ /* toggle IPU double-buffer index */
+ priv->ipu_buf_num ^= 1;
+
+ /* bump the EOF timeout timer */
+ mod_timer(&priv->eof_timeout_timer,
+ jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+
+unlock:
+ spin_unlock(&priv->irqlock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t csi_idmac_nfb4eof_interrupt(int irq, void *dev_id)
+{
+ struct csi_priv *priv = dev_id;
+
+ spin_lock(&priv->irqlock);
+
+ /*
+ * this is not an unrecoverable error, just mark
+ * the next captured frame with vb2 error flag.
+ */
+ priv->nfb4eof = true;
+
+ v4l2_err(&priv->sd, "NFB4EOF\n");
+
+ spin_unlock(&priv->irqlock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function. This is an unrecoverable condition
+ * without a stream restart.
+ */
+static void csi_idmac_eof_timeout(unsigned long data)
+{
+ struct csi_priv *priv = (struct csi_priv *)data;
+ struct imx_media_video_dev *vdev = priv->vdev;
+
+ v4l2_err(&priv->sd, "EOF timeout\n");
+
+ /* signal a fatal error to capture device */
+ imx_media_capture_device_error(vdev);
+}
+
+static void csi_idmac_setup_vb2_buf(struct csi_priv *priv, dma_addr_t *phys)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct imx_media_buffer *buf;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ buf = imx_media_capture_device_next_buf(vdev);
+ if (buf) {
+ priv->active_vb2_buf[i] = buf;
+ phys[i] = vb2_dma_contig_plane_dma_addr(
+ &buf->vbuf.vb2_buf, 0);
+ } else {
+ priv->active_vb2_buf[i] = NULL;
+ phys[i] = priv->underrun_buf.phys;
+ }
+ }
+}
+
+static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv,
+ enum vb2_buffer_state return_status)
+{
+ struct imx_media_buffer *buf;
+ int i;
+
+ /* return any remaining active frames with return_status */
+ for (i = 0; i < 2; i++) {
+ buf = priv->active_vb2_buf[i];
+ if (buf) {
+ struct vb2_buffer *vb = &buf->vbuf.vb2_buf;
+
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, return_status);
+ }
+ }
+}
+
+/* init the SMFC IDMAC channel */
+static int csi_idmac_setup_channel(struct csi_priv *priv)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct v4l2_fwnode_endpoint *sensor_ep;
+ struct v4l2_mbus_framefmt *infmt;
+ struct ipu_image image;
+ u32 passthrough_bits;
+ dma_addr_t phys[2];
+ bool passthrough;
+ u32 burst_size;
+ int ret;
+
+ infmt = &priv->format_mbus[CSI_SINK_PAD];
+ sensor_ep = &priv->sensor->sensor_ep;
+
+ ipu_cpmem_zero(priv->idmac_ch);
+
+ memset(&image, 0, sizeof(image));
+ image.pix = vdev->fmt.fmt.pix;
+ image.rect.width = image.pix.width;
+ image.rect.height = image.pix.height;
+
+ csi_idmac_setup_vb2_buf(priv, phys);
+
+ image.phys0 = phys[0];
+ image.phys1 = phys[1];
+
+ /*
+ * Check for conditions that require the IPU to handle the
+ * data internally as generic data, aka passthrough mode:
+ * - raw bayer formats
+ * - the sensor bus is 16-bit parallel
+ */
+ switch (image.pix.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ burst_size = 8;
+ passthrough = true;
+ passthrough_bits = 8;
+ break;
+ case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_PIX_FMT_SGBRG16:
+ case V4L2_PIX_FMT_SGRBG16:
+ case V4L2_PIX_FMT_SRGGB16:
+ burst_size = 4;
+ passthrough = true;
+ passthrough_bits = 16;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ burst_size = (image.pix.width & 0x3f) ?
+ ((image.pix.width & 0x1f) ?
+ ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
+ passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
+ sensor_ep->bus.parallel.bus_width >= 16);
+ passthrough_bits = 16;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ burst_size = (image.pix.width & 0x1f) ?
+ ((image.pix.width & 0xf) ? 8 : 16) : 32;
+ passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
+ sensor_ep->bus.parallel.bus_width >= 16);
+ passthrough_bits = 16;
+ break;
+ default:
+ burst_size = (image.pix.width & 0xf) ? 8 : 16;
+ passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
+ sensor_ep->bus.parallel.bus_width >= 16);
+ passthrough_bits = 16;
+ break;
+ }
+
+ if (passthrough) {
+ ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width,
+ image.rect.height);
+ ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline);
+ ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0);
+ ipu_cpmem_set_buffer(priv->idmac_ch, 1, image.phys1);
+ ipu_cpmem_set_format_passthrough(priv->idmac_ch,
+ passthrough_bits);
+ } else {
+ ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
+ if (ret)
+ goto unsetup_vb2;
+ }
+
+ ipu_cpmem_set_burstsize(priv->idmac_ch, burst_size);
+
+ /*
+ * Set the channel for the direct CSI-->memory via SMFC
+ * use-case to very high priority, by enabling the watermark
+ * signal in the SMFC, enabling WM in the channel, and setting
+ * the channel priority to high.
+ *
+ * Refer to the i.mx6 rev. D TRM Table 36-8: Calculated priority
+ * value.
+ *
+ * The WM's are set very low by intention here to ensure that
+ * the SMFC FIFOs do not overflow.
+ */
+ ipu_smfc_set_watermark(priv->smfc, 0x02, 0x01);
+ ipu_cpmem_set_high_priority(priv->idmac_ch);
+ ipu_idmac_enable_watermark(priv->idmac_ch, true);
+ ipu_cpmem_set_axi_id(priv->idmac_ch, 0);
+
+ burst_size = passthrough ?
+ (burst_size >> 3) - 1 : (burst_size >> 2) - 1;
+
+ ipu_smfc_set_burstsize(priv->smfc, burst_size);
+
+ if (image.pix.field == V4L2_FIELD_NONE &&
+ V4L2_FIELD_HAS_BOTH(infmt->field))
+ ipu_cpmem_interlaced_scan(priv->idmac_ch,
+ image.pix.bytesperline);
+
+ ipu_idmac_set_double_buffer(priv->idmac_ch, true);
+
+ return 0;
+
+unsetup_vb2:
+ csi_idmac_unsetup_vb2_buf(priv, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void csi_idmac_unsetup(struct csi_priv *priv,
+ enum vb2_buffer_state state)
+{
+ ipu_idmac_disable_channel(priv->idmac_ch);
+ ipu_smfc_disable(priv->smfc);
+
+ csi_idmac_unsetup_vb2_buf(priv, state);
+}
+
+static int csi_idmac_setup(struct csi_priv *priv)
+{
+ int ret;
+
+ ret = csi_idmac_setup_channel(priv);
+ if (ret)
+ return ret;
+
+ ipu_cpmem_dump(priv->idmac_ch);
+ ipu_dump(priv->ipu);
+
+ ipu_smfc_enable(priv->smfc);
+
+ /* set buffers ready */
+ ipu_idmac_select_buffer(priv->idmac_ch, 0);
+ ipu_idmac_select_buffer(priv->idmac_ch, 1);
+
+ /* enable the channels */
+ ipu_idmac_enable_channel(priv->idmac_ch);
+
+ return 0;
+}
+
+static int csi_idmac_start(struct csi_priv *priv)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ struct v4l2_pix_format *outfmt;
+ int ret;
+
+ ret = csi_idmac_get_ipu_resources(priv);
+ if (ret)
+ return ret;
+
+ ipu_smfc_map_channel(priv->smfc, priv->csi_id, priv->vc_num);
+
+ outfmt = &vdev->fmt.fmt.pix;
+
+ ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf,
+ outfmt->sizeimage);
+ if (ret)
+ goto out_put_ipu;
+
+ priv->ipu_buf_num = 0;
+
+ /* init EOF completion waitq */
+ init_completion(&priv->last_eof_comp);
+ priv->last_eof = false;
+ priv->nfb4eof = false;
+
+ ret = csi_idmac_setup(priv);
+ if (ret) {
+ v4l2_err(&priv->sd, "csi_idmac_setup failed: %d\n", ret);
+ goto out_free_dma_buf;
+ }
+
+ priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+ priv->idmac_ch,
+ IPU_IRQ_NFB4EOF);
+ ret = devm_request_irq(priv->dev, priv->nfb4eof_irq,
+ csi_idmac_nfb4eof_interrupt, 0,
+ "imx-smfc-nfb4eof", priv);
+ if (ret) {
+ v4l2_err(&priv->sd,
+ "Error registering NFB4EOF irq: %d\n", ret);
+ goto out_unsetup;
+ }
+
+ priv->eof_irq = ipu_idmac_channel_irq(priv->ipu, priv->idmac_ch,
+ IPU_IRQ_EOF);
+
+ ret = devm_request_irq(priv->dev, priv->eof_irq,
+ csi_idmac_eof_interrupt, 0,
+ "imx-smfc-eof", priv);
+ if (ret) {
+ v4l2_err(&priv->sd,
+ "Error registering eof irq: %d\n", ret);
+ goto out_free_nfb4eof_irq;
+ }
+
+ /* start the EOF timeout timer */
+ mod_timer(&priv->eof_timeout_timer,
+ jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+
+ return 0;
+
+out_free_nfb4eof_irq:
+ devm_free_irq(priv->dev, priv->nfb4eof_irq, priv);
+out_unsetup:
+ csi_idmac_unsetup(priv, VB2_BUF_STATE_QUEUED);
+out_free_dma_buf:
+ imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+out_put_ipu:
+ csi_idmac_put_ipu_resources(priv);
+ return ret;
+}
+
+static void csi_idmac_stop(struct csi_priv *priv)
+{
+ unsigned long flags;
+ int ret;
+
+ /* mark next EOF interrupt as the last before stream off */
+ spin_lock_irqsave(&priv->irqlock, flags);
+ priv->last_eof = true;
+ spin_unlock_irqrestore(&priv->irqlock, flags);
+
+ /*
+ * and then wait for interrupt handler to mark completion.
+ */
+ ret = wait_for_completion_timeout(
+ &priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+ if (ret == 0)
+ v4l2_warn(&priv->sd, "wait last EOF timeout\n");
+
+ devm_free_irq(priv->dev, priv->eof_irq, priv);
+ devm_free_irq(priv->dev, priv->nfb4eof_irq, priv);
+
+ csi_idmac_unsetup(priv, VB2_BUF_STATE_ERROR);
+
+ imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+
+ /* cancel the EOF timeout timer */
+ del_timer_sync(&priv->eof_timeout_timer);
+
+ csi_idmac_put_ipu_resources(priv);
+}
+
+/* Update the CSI whole sensor and active windows */
+static int csi_setup(struct csi_priv *priv)
+{
+ struct v4l2_mbus_framefmt *infmt, *outfmt;
+ struct v4l2_mbus_config sensor_mbus_cfg;
+ struct v4l2_fwnode_endpoint *sensor_ep;
+ struct v4l2_mbus_framefmt if_fmt;
+
+ infmt = &priv->format_mbus[CSI_SINK_PAD];
+ outfmt = &priv->format_mbus[priv->active_output_pad];
+ sensor_ep = &priv->sensor->sensor_ep;
+
+ /* compose mbus_config from sensor endpoint */
+ sensor_mbus_cfg.type = sensor_ep->bus_type;
+ sensor_mbus_cfg.flags = (sensor_ep->bus_type == V4L2_MBUS_CSI2) ?
+ sensor_ep->bus.mipi_csi2.flags :
+ sensor_ep->bus.parallel.flags;
+
+ /*
+ * we need to pass input sensor frame to CSI interface, but
+ * with translated field type from output format
+ */
+ if_fmt = *infmt;
+ if_fmt.field = outfmt->field;
+
+ ipu_csi_set_window(priv->csi, &priv->crop);
+
+ ipu_csi_set_downsize(priv->csi,
+ priv->crop.width == 2 * priv->compose.width,
+ priv->crop.height == 2 * priv->compose.height);
+
+ ipu_csi_init_interface(priv->csi, &sensor_mbus_cfg, &if_fmt);
+
+ ipu_csi_set_dest(priv->csi, priv->dest);
+
+ if (priv->dest == IPU_CSI_DEST_IDMAC)
+ ipu_csi_set_skip_smfc(priv->csi, priv->skip->skip_smfc,
+ priv->skip->max_ratio - 1, 0);
+
+ ipu_csi_dump(priv->csi);
+
+ return 0;
+}
+
+static int csi_start(struct csi_priv *priv)
+{
+ struct v4l2_fract *output_fi, *input_fi;
+ u32 bad_frames = 0;
+ int ret;
+
+ if (!priv->sensor) {
+ v4l2_err(&priv->sd, "no sensor attached\n");
+ return -EINVAL;
+ }
+
+ output_fi = &priv->frame_interval[priv->active_output_pad];
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
+
+ ret = v4l2_subdev_call(priv->sensor->sd, sensor,
+ g_skip_frames, &bad_frames);
+ if (!ret && bad_frames) {
+ u32 delay_usec;
+
+ /*
+ * This sensor has bad frames when it is turned on,
+ * add a delay to avoid them before enabling the CSI
+ * hardware. Especially for sensors with a bt.656 interface,
+ * any shifts in the SAV/EAV sync codes will cause the CSI
+ * to lose vert/horiz sync.
+ */
+ delay_usec = DIV_ROUND_UP_ULL(
+ (u64)USEC_PER_SEC * input_fi->numerator * bad_frames,
+ input_fi->denominator);
+ usleep_range(delay_usec, delay_usec + 1000);
+ }
+
+ if (priv->dest == IPU_CSI_DEST_IDMAC) {
+ ret = csi_idmac_start(priv);
+ if (ret)
+ return ret;
+ }
+
+ ret = csi_setup(priv);
+ if (ret)
+ goto idmac_stop;
+
+ /* start the frame interval monitor */
+ if (priv->fim && priv->dest == IPU_CSI_DEST_IDMAC) {
+ ret = imx_media_fim_set_stream(priv->fim, output_fi, true);
+ if (ret)
+ goto idmac_stop;
+ }
+
+ ret = ipu_csi_enable(priv->csi);
+ if (ret) {
+ v4l2_err(&priv->sd, "CSI enable error: %d\n", ret);
+ goto fim_off;
+ }
+
+ return 0;
+
+fim_off:
+ if (priv->fim && priv->dest == IPU_CSI_DEST_IDMAC)
+ imx_media_fim_set_stream(priv->fim, NULL, false);
+idmac_stop:
+ if (priv->dest == IPU_CSI_DEST_IDMAC)
+ csi_idmac_stop(priv);
+ return ret;
+}
+
+static void csi_stop(struct csi_priv *priv)
+{
+ if (priv->dest == IPU_CSI_DEST_IDMAC) {
+ csi_idmac_stop(priv);
+
+ /* stop the frame interval monitor */
+ if (priv->fim)
+ imx_media_fim_set_stream(priv->fim, NULL, false);
+ }
+
+ ipu_csi_disable(priv->csi);
+}
+
+static const struct csi_skip_desc csi_skip[12] = {
+ { 1, 1, 0x00 }, /* Keep all frames */
+ { 5, 6, 0x10 }, /* Skip every sixth frame */
+ { 4, 5, 0x08 }, /* Skip every fifth frame */
+ { 3, 4, 0x04 }, /* Skip every fourth frame */
+ { 2, 3, 0x02 }, /* Skip every third frame */
+ { 3, 5, 0x0a }, /* Skip frames 1 and 3 of every 5 */
+ { 1, 2, 0x01 }, /* Skip every second frame */
+ { 2, 5, 0x0b }, /* Keep frames 1 and 4 of every 5 */
+ { 1, 3, 0x03 }, /* Keep one in three frames */
+ { 1, 4, 0x07 }, /* Keep one in four frames */
+ { 1, 5, 0x0f }, /* Keep one in five frames */
+ { 1, 6, 0x1f }, /* Keep one in six frames */
+};
+
+static void csi_apply_skip_interval(const struct csi_skip_desc *skip,
+ struct v4l2_fract *interval)
+{
+ unsigned int div;
+
+ interval->numerator *= skip->max_ratio;
+ interval->denominator *= skip->keep;
+
+ /* Reduce fraction to lowest terms */
+ div = gcd(interval->numerator, interval->denominator);
+ if (div > 1) {
+ interval->numerator /= div;
+ interval->denominator /= div;
+ }
+}
+
+/*
+ * Find the skip pattern to produce the output frame interval closest to the
+ * requested one, for the given input frame interval. Updates the output frame
+ * interval to the exact value.
+ */
+static const struct csi_skip_desc *csi_find_best_skip(struct v4l2_fract *in,
+ struct v4l2_fract *out)
+{
+ const struct csi_skip_desc *skip = &csi_skip[0], *best_skip = skip;
+ u32 min_err = UINT_MAX;
+ u64 want_us;
+ int i;
+
+ /* Default to 1:1 ratio */
+ if (out->numerator == 0 || out->denominator == 0 ||
+ in->numerator == 0 || in->denominator == 0) {
+ *out = *in;
+ return best_skip;
+ }
+
+ want_us = div_u64((u64)USEC_PER_SEC * out->numerator, out->denominator);
+
+ /* Find the reduction closest to the requested time per frame */
+ for (i = 0; i < ARRAY_SIZE(csi_skip); i++, skip++) {
+ u64 tmp, err;
+
+ tmp = div_u64((u64)USEC_PER_SEC * in->numerator *
+ skip->max_ratio, in->denominator * skip->keep);
+
+ err = abs((s64)tmp - want_us);
+ if (err < min_err) {
+ min_err = err;
+ best_skip = skip;
+ }
+ }
+
+ *out = *in;
+ csi_apply_skip_interval(best_skip, out);
+
+ return best_skip;
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int csi_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+
+ if (fi->pad >= CSI_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fi->interval = priv->frame_interval[fi->pad];
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int csi_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
+
+ switch (fi->pad) {
+ case CSI_SINK_PAD:
+ /* No limits on input frame interval */
+ /* Reset output intervals and frame skipping ratio to 1:1 */
+ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval;
+ priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval;
+ priv->skip = &csi_skip[0];
+ break;
+ case CSI_SRC_PAD_IDMAC:
+ /*
+ * frame interval at IDMAC output pad depends on input
+ * interval, modified by frame skipping.
+ */
+ priv->skip = csi_find_best_skip(input_fi, &fi->interval);
+ break;
+ case CSI_SRC_PAD_DIRECT:
+ /*
+ * frame interval at DIRECT output pad is same as input
+ * interval.
+ */
+ fi->interval = *input_fi;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->frame_interval[fi->pad] = fi->interval;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->src_sd || !priv->sink) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (priv->stream_count != !enable)
+ goto update_count;
+
+ if (enable) {
+ /* upstream must be started first, before starting CSI */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret)
+ goto out;
+
+ dev_dbg(priv->dev, "stream ON\n");
+ ret = csi_start(priv);
+ if (ret) {
+ v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
+ goto out;
+ }
+ } else {
+ dev_dbg(priv->dev, "stream OFF\n");
+ /* CSI must be stopped first, then stop upstream */
+ csi_stop(priv);
+ v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
+ }
+
+update_count:
+ priv->stream_count += enable ? 1 : -1;
+ if (priv->stream_count < 0)
+ priv->stream_count = 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(priv->dev, "link setup %s -> %s\n", remote->entity->name,
+ local->entity->name);
+
+ mutex_lock(&priv->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SINK) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->src_sd = remote_sd;
+ } else {
+ priv->src_sd = NULL;
+ }
+
+ goto out;
+ }
+
+ /* this is a source pad */
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->sink) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+ v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
+ priv->sink = NULL;
+ goto out;
+ }
+
+ /* record which output pad is now active */
+ priv->active_output_pad = local->index;
+
+ /* set CSI destination */
+ if (local->index == CSI_SRC_PAD_IDMAC) {
+ if (!is_media_entity_v4l2_video_device(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (priv->fim) {
+ ret = imx_media_fim_add_controls(priv->fim);
+ if (ret)
+ goto out;
+ }
+
+ priv->dest = IPU_CSI_DEST_IDMAC;
+ } else {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+ switch (remote_sd->grp_id) {
+ case IMX_MEDIA_GRP_ID_VDIC:
+ priv->dest = IPU_CSI_DEST_VDIC;
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRP:
+ priv->dest = IPU_CSI_DEST_IC;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ priv->sink = remote->entity;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fwnode_endpoint *sensor_ep;
+ const struct imx_media_pixfmt *incc;
+ struct imx_media_subdev *sensor;
+ bool is_csi2;
+ int ret;
+
+ ret = v4l2_subdev_link_validate_default(sd, link,
+ source_fmt, sink_fmt);
+ if (ret)
+ return ret;
+
+ sensor = __imx_media_find_sensor(priv->md, &priv->sd.entity);
+ if (IS_ERR(sensor)) {
+ v4l2_err(&priv->sd, "no sensor attached\n");
+ return PTR_ERR(priv->sensor);
+ }
+
+ mutex_lock(&priv->lock);
+
+ priv->sensor = sensor;
+ sensor_ep = &priv->sensor->sensor_ep;
+ is_csi2 = (sensor_ep->bus_type == V4L2_MBUS_CSI2);
+ incc = priv->cc[CSI_SINK_PAD];
+
+ if (priv->dest != IPU_CSI_DEST_IDMAC &&
+ (incc->bayer || (!is_csi2 &&
+ sensor_ep->bus.parallel.bus_width >= 16))) {
+ v4l2_err(&priv->sd,
+ "bayer/16-bit parallel buses must go to IDMAC pad\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (is_csi2) {
+ int vc_num = 0;
+ /*
+ * NOTE! It seems the virtual channels from the mipi csi-2
+ * receiver are used only for routing by the video mux's,
+ * or for hard-wired routing to the CSI's. Once the stream
+ * enters the CSI's however, they are treated internally
+ * in the IPU as virtual channel 0.
+ */
+#if 0
+ mutex_unlock(&priv->lock);
+ vc_num = imx_media_find_mipi_csi2_channel(priv->md,
+ &priv->sd.entity);
+ if (vc_num < 0)
+ return vc_num;
+ mutex_lock(&priv->lock);
+#endif
+ ipu_csi_set_mipi_datatype(priv->csi, vc_num,
+ &priv->format_mbus[CSI_SINK_PAD]);
+ }
+
+ /* select either parallel or MIPI-CSI2 as input to CSI */
+ ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
+ else
+ return &priv->format_mbus[pad];
+}
+
+static struct v4l2_rect *
+__csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(&priv->sd, cfg, CSI_SINK_PAD);
+ else
+ return &priv->crop;
+}
+
+static struct v4l2_rect *
+__csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_compose(&priv->sd, cfg,
+ CSI_SINK_PAD);
+ else
+ return &priv->compose;
+}
+
+static void csi_try_crop(struct csi_priv *priv,
+ struct v4l2_rect *crop,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_mbus_framefmt *infmt,
+ struct imx_media_subdev *sensor)
+{
+ struct v4l2_fwnode_endpoint *sensor_ep;
+
+ sensor_ep = &sensor->sensor_ep;
+
+ crop->width = min_t(__u32, infmt->width, crop->width);
+ if (crop->left + crop->width > infmt->width)
+ crop->left = infmt->width - crop->width;
+ /* adjust crop left/width to h/w alignment restrictions */
+ crop->left &= ~0x3;
+ crop->width &= ~0x7;
+
+ /*
+ * FIXME: not sure why yet, but on interlaced bt.656,
+ * changing the vertical cropping causes loss of vertical
+ * sync, so fix it to NTSC/PAL active lines. NTSC contains
+ * 2 extra lines of active video that need to be cropped.
+ */
+ if (sensor_ep->bus_type == V4L2_MBUS_BT656 &&
+ (V4L2_FIELD_HAS_BOTH(infmt->field) ||
+ infmt->field == V4L2_FIELD_ALTERNATE)) {
+ crop->height = infmt->height;
+ crop->top = (infmt->height == 480) ? 2 : 0;
+ } else {
+ crop->height = min_t(__u32, infmt->height, crop->height);
+ if (crop->top + crop->height > infmt->height)
+ crop->top = infmt->height - crop->height;
+ }
+}
+
+static int csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ const struct imx_media_pixfmt *incc;
+ struct v4l2_mbus_framefmt *infmt;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, code->which);
+ incc = imx_media_find_mbus_format(infmt->code, CS_SEL_ANY, true);
+
+ switch (code->pad) {
+ case CSI_SINK_PAD:
+ ret = imx_media_enum_mbus_format(&code->code, code->index,
+ CS_SEL_ANY, true);
+ break;
+ case CSI_SRC_PAD_DIRECT:
+ case CSI_SRC_PAD_IDMAC:
+ if (incc->bayer) {
+ if (code->index != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ code->code = infmt->code;
+ } else {
+ u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+ ret = imx_media_enum_ipu_format(&code->code,
+ code->index,
+ cs_sel);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (fse->pad >= CSI_NUM_PADS ||
+ fse->index > (fse->pad == CSI_SINK_PAD ? 0 : 3))
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (fse->pad == CSI_SINK_PAD) {
+ fse->min_width = MIN_W;
+ fse->max_width = MAX_W;
+ fse->min_height = MIN_H;
+ fse->max_height = MAX_H;
+ } else {
+ crop = __csi_get_crop(priv, cfg, fse->which);
+
+ fse->min_width = fse->max_width = fse->index & 1 ?
+ crop->width / 2 : crop->width;
+ fse->min_height = fse->max_height = fse->index & 2 ?
+ crop->height / 2 : crop->height;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (fie->pad >= CSI_NUM_PADS ||
+ fie->index >= (fie->pad != CSI_SRC_PAD_IDMAC ?
+ 1 : ARRAY_SIZE(csi_skip)))
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
+ crop = __csi_get_crop(priv, cfg, fie->which);
+
+ if ((fie->width != crop->width && fie->width != crop->width / 2) ||
+ (fie->height != crop->height && fie->height != crop->height / 2)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fie->interval = *input_fi;
+
+ if (fie->pad == CSI_SRC_PAD_IDMAC)
+ csi_apply_skip_interval(&csi_skip[fie->index],
+ &fie->interval);
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= CSI_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdformat->format = *fmt;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static void csi_try_fmt(struct csi_priv *priv,
+ struct imx_media_subdev *sensor,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat,
+ struct v4l2_rect *crop,
+ struct v4l2_rect *compose,
+ const struct imx_media_pixfmt **cc)
+{
+ const struct imx_media_pixfmt *incc;
+ struct v4l2_mbus_framefmt *infmt;
+ u32 code;
+
+ infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which);
+
+ switch (sdformat->pad) {
+ case CSI_SRC_PAD_DIRECT:
+ case CSI_SRC_PAD_IDMAC:
+ incc = imx_media_find_mbus_format(infmt->code,
+ CS_SEL_ANY, true);
+
+ sdformat->format.width = compose->width;
+ sdformat->format.height = compose->height;
+
+ if (incc->bayer) {
+ sdformat->format.code = infmt->code;
+ *cc = incc;
+ } else {
+ u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+
+ *cc = imx_media_find_ipu_format(sdformat->format.code,
+ cs_sel);
+ if (!*cc) {
+ imx_media_enum_ipu_format(&code, 0, cs_sel);
+ *cc = imx_media_find_ipu_format(code, cs_sel);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+ }
+
+ if (sdformat->pad == CSI_SRC_PAD_DIRECT ||
+ sdformat->format.field != V4L2_FIELD_NONE)
+ sdformat->format.field = infmt->field;
+
+ /*
+ * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT
+ * depending on input height (assume NTSC top-bottom
+ * order if 480 lines, otherwise PAL bottom-top order).
+ */
+ if (sdformat->format.field == V4L2_FIELD_ALTERNATE) {
+ sdformat->format.field = (infmt->height == 480) ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
+ }
+
+ /* propagate colorimetry from sink */
+ sdformat->format.colorspace = infmt->colorspace;
+ sdformat->format.xfer_func = infmt->xfer_func;
+ sdformat->format.quantization = infmt->quantization;
+ sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
+ break;
+ case CSI_SINK_PAD:
+ v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
+ W_ALIGN, &sdformat->format.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+
+ /* Reset crop and compose rectangles */
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = sdformat->format.width;
+ crop->height = sdformat->format.height;
+ csi_try_crop(priv, crop, cfg, &sdformat->format, sensor);
+ compose->left = 0;
+ compose->top = 0;
+ compose->width = crop->width;
+ compose->height = crop->height;
+
+ *cc = imx_media_find_mbus_format(sdformat->format.code,
+ CS_SEL_ANY, true);
+ if (!*cc) {
+ imx_media_enum_mbus_format(&code, 0,
+ CS_SEL_ANY, false);
+ *cc = imx_media_find_mbus_format(code,
+ CS_SEL_ANY, false);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ imx_media_fill_default_mbus_fields(
+ &sdformat->format, infmt,
+ priv->active_output_pad == CSI_SRC_PAD_DIRECT);
+ break;
+ }
+}
+
+static int csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct imx_media_video_dev *vdev = priv->vdev;
+ const struct imx_media_pixfmt *cc;
+ struct imx_media_subdev *sensor;
+ struct v4l2_pix_format vdev_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop, *compose;
+ int ret = 0;
+
+ if (sdformat->pad >= CSI_NUM_PADS)
+ return -EINVAL;
+
+ sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
+ if (IS_ERR(sensor)) {
+ v4l2_err(&priv->sd, "no sensor attached\n");
+ return PTR_ERR(sensor);
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ crop = __csi_get_crop(priv, cfg, sdformat->which);
+ compose = __csi_get_compose(priv, cfg, sdformat->which);
+
+ csi_try_fmt(priv, sensor, cfg, sdformat, crop, compose, &cc);
+
+ fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ *fmt = sdformat->format;
+
+ if (sdformat->pad == CSI_SINK_PAD) {
+ int pad;
+
+ /* propagate format to source pads */
+ for (pad = CSI_SINK_PAD + 1; pad < CSI_NUM_PADS; pad++) {
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *outfmt;
+ struct v4l2_subdev_format format;
+
+ format.pad = pad;
+ format.which = sdformat->which;
+ format.format = sdformat->format;
+ csi_try_fmt(priv, sensor, cfg, &format, NULL, compose,
+ &outcc);
+
+ outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which);
+ *outfmt = format.format;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[pad] = outcc;
+ }
+ }
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ goto out;
+
+ priv->cc[sdformat->pad] = cc;
+
+ /* propagate IDMAC output pad format to capture device */
+ imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt,
+ &priv->format_mbus[CSI_SRC_PAD_IDMAC],
+ priv->cc[CSI_SRC_PAD_IDMAC]);
+ mutex_unlock(&priv->lock);
+ imx_media_capture_device_set_format(vdev, &vdev_fmt);
+
+ return 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_rect *crop, *compose;
+ int ret = 0;
+
+ if (sel->pad != CSI_SINK_PAD)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which);
+ crop = __csi_get_crop(priv, cfg, sel->which);
+ compose = __csi_get_compose(priv, cfg, sel->which);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = infmt->width;
+ sel->r.height = infmt->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = crop->width;
+ sel->r.height = crop->height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = *compose;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_set_scale(u32 *compose, u32 crop, u32 flags)
+{
+ if ((flags & (V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE)) ==
+ (V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE) &&
+ *compose != crop && *compose != crop / 2)
+ return -ERANGE;
+
+ if (*compose <= crop / 2 ||
+ (*compose < crop * 3 / 4 && !(flags & V4L2_SEL_FLAG_GE)) ||
+ (*compose < crop && (flags & V4L2_SEL_FLAG_LE)))
+ *compose = crop / 2;
+ else
+ *compose = crop;
+
+ return 0;
+}
+
+static int csi_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_rect *crop, *compose;
+ struct imx_media_subdev *sensor;
+ int pad, ret = 0;
+
+ if (sel->pad != CSI_SINK_PAD)
+ return -EINVAL;
+
+ sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
+ if (IS_ERR(sensor)) {
+ v4l2_err(&priv->sd, "no sensor attached\n");
+ return PTR_ERR(sensor);
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sel->which);
+ crop = __csi_get_crop(priv, cfg, sel->which);
+ compose = __csi_get_compose(priv, cfg, sel->which);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ /*
+ * Modifying the crop rectangle always changes the format on
+ * the source pads. If the KEEP_CONFIG flag is set, just return
+ * the current crop rectangle.
+ */
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = priv->crop;
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
+ *crop = sel->r;
+ goto out;
+ }
+
+ csi_try_crop(priv, &sel->r, cfg, infmt, sensor);
+
+ *crop = sel->r;
+
+ /* Reset scaling to 1:1 */
+ compose->width = crop->width;
+ compose->height = crop->height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ /*
+ * Modifying the compose rectangle always changes the format on
+ * the source pads. If the KEEP_CONFIG flag is set, just return
+ * the current compose rectangle.
+ */
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = priv->compose;
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
+ *compose = sel->r;
+ goto out;
+ }
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ ret = csi_set_scale(&sel->r.width, crop->width, sel->flags);
+ if (ret)
+ goto out;
+ ret = csi_set_scale(&sel->r.height, crop->height, sel->flags);
+ if (ret)
+ goto out;
+
+ *compose = sel->r;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Reset source pads to sink compose rectangle */
+ for (pad = CSI_SINK_PAD + 1; pad < CSI_NUM_PADS; pad++) {
+ struct v4l2_mbus_framefmt *outfmt;
+
+ outfmt = __csi_get_fmt(priv, cfg, pad, sel->which);
+ outfmt->width = compose->width;
+ outfmt->height = compose->height;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR)
+ return -EINVAL;
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static int csi_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int csi_registered(struct v4l2_subdev *sd)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ int i, ret;
+ u32 code;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ /* get handle to IPU CSI */
+ priv->csi = ipu_csi_get(priv->ipu, priv->csi_id);
+ if (IS_ERR(priv->csi)) {
+ v4l2_err(&priv->sd, "failed to get CSI%d\n", priv->csi_id);
+ return PTR_ERR(priv->csi);
+ }
+
+ for (i = 0; i < CSI_NUM_PADS; i++) {
+ priv->pad[i].flags = (i == CSI_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ code = 0;
+ if (i != CSI_SINK_PAD)
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
+ 640, 480, code, V4L2_FIELD_NONE,
+ &priv->cc[i]);
+ if (ret)
+ goto put_csi;
+
+ /* init default frame interval */
+ priv->frame_interval[i].numerator = 1;
+ priv->frame_interval[i].denominator = 30;
+ }
+
+ /* disable frame skipping */
+ priv->skip = &csi_skip[0];
+
+ /* init default crop and compose rectangle sizes */
+ priv->crop.width = 640;
+ priv->crop.height = 480;
+ priv->compose.width = 640;
+ priv->compose.height = 480;
+
+ priv->fim = imx_media_fim_init(&priv->sd);
+ if (IS_ERR(priv->fim)) {
+ ret = PTR_ERR(priv->fim);
+ goto put_csi;
+ }
+
+ ret = media_entity_pads_init(&sd->entity, CSI_NUM_PADS, priv->pad);
+ if (ret)
+ goto free_fim;
+
+ ret = imx_media_capture_device_register(priv->vdev);
+ if (ret)
+ goto free_fim;
+
+ ret = imx_media_add_video_device(priv->md, priv->vdev);
+ if (ret)
+ goto unreg;
+
+ return 0;
+unreg:
+ imx_media_capture_device_unregister(priv->vdev);
+free_fim:
+ if (priv->fim)
+ imx_media_fim_free(priv->fim);
+put_csi:
+ ipu_csi_put(priv->csi);
+ return ret;
+}
+
+static void csi_unregistered(struct v4l2_subdev *sd)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+
+ imx_media_capture_device_unregister(priv->vdev);
+
+ if (priv->fim)
+ imx_media_fim_free(priv->fim);
+
+ if (!IS_ERR_OR_NULL(priv->csi))
+ ipu_csi_put(priv->csi);
+}
+
+static const struct media_entity_operations csi_entity_ops = {
+ .link_setup = csi_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_core_ops csi_core_ops = {
+ .subscribe_event = csi_subscribe_event,
+ .unsubscribe_event = csi_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops csi_video_ops = {
+ .g_frame_interval = csi_g_frame_interval,
+ .s_frame_interval = csi_s_frame_interval,
+ .s_stream = csi_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csi_pad_ops = {
+ .enum_mbus_code = csi_enum_mbus_code,
+ .enum_frame_size = csi_enum_frame_size,
+ .enum_frame_interval = csi_enum_frame_interval,
+ .get_fmt = csi_get_fmt,
+ .set_fmt = csi_set_fmt,
+ .get_selection = csi_get_selection,
+ .set_selection = csi_set_selection,
+ .link_validate = csi_link_validate,
+};
+
+static const struct v4l2_subdev_ops csi_subdev_ops = {
+ .core = &csi_core_ops,
+ .video = &csi_video_ops,
+ .pad = &csi_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csi_internal_ops = {
+ .registered = csi_registered,
+ .unregistered = csi_unregistered,
+};
+
+static int imx_csi_probe(struct platform_device *pdev)
+{
+ struct ipu_client_platformdata *pdata;
+ struct pinctrl *pinctrl;
+ struct csi_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, &priv->sd);
+ priv->dev = &pdev->dev;
+
+ ret = dma_set_coherent_mask(priv->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ /* get parent IPU */
+ priv->ipu = dev_get_drvdata(priv->dev->parent);
+
+ /* get our CSI id */
+ pdata = priv->dev->platform_data;
+ priv->csi_id = pdata->csi;
+ priv->smfc_id = (priv->csi_id == 0) ? 0 : 2;
+
+ init_timer(&priv->eof_timeout_timer);
+ priv->eof_timeout_timer.data = (unsigned long)priv;
+ priv->eof_timeout_timer.function = csi_idmac_eof_timeout;
+ spin_lock_init(&priv->irqlock);
+
+ v4l2_subdev_init(&priv->sd, &csi_subdev_ops);
+ v4l2_set_subdevdata(&priv->sd, priv);
+ priv->sd.internal_ops = &csi_internal_ops;
+ priv->sd.entity.ops = &csi_entity_ops;
+ priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ priv->sd.dev = &pdev->dev;
+ priv->sd.fwnode = of_fwnode_handle(pdata->of_node);
+ priv->sd.owner = THIS_MODULE;
+ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ priv->sd.grp_id = priv->csi_id ?
+ IMX_MEDIA_GRP_ID_CSI1 : IMX_MEDIA_GRP_ID_CSI0;
+ imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name),
+ priv->sd.grp_id, ipu_get_num(priv->ipu));
+
+ priv->vdev = imx_media_capture_device_init(&priv->sd,
+ CSI_SRC_PAD_IDMAC);
+ if (IS_ERR(priv->vdev))
+ return PTR_ERR(priv->vdev);
+
+ mutex_init(&priv->lock);
+
+ v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
+ priv->sd.ctrl_handler = &priv->ctrl_hdlr;
+
+ /*
+ * The IPUv3 driver did not assign an of_node to this
+ * device. As a result, pinctrl does not automatically
+ * configure our pin groups, so we need to do that manually
+ * here, after setting this device's of_node.
+ */
+ priv->dev->of_node = pdata->of_node;
+ pinctrl = devm_pinctrl_get_select_default(priv->dev);
+
+ ret = v4l2_async_register_subdev(&priv->sd);
+ if (ret)
+ goto free;
+
+ return 0;
+free:
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+ mutex_destroy(&priv->lock);
+ imx_media_capture_device_remove(priv->vdev);
+ return ret;
+}
+
+static int imx_csi_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi_priv *priv = sd_to_dev(sd);
+
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+ mutex_destroy(&priv->lock);
+ imx_media_capture_device_remove(priv->vdev);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct platform_device_id imx_csi_ids[] = {
+ { .name = "imx-ipuv3-csi" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, imx_csi_ids);
+
+static struct platform_driver imx_csi_driver = {
+ .probe = imx_csi_probe,
+ .remove = imx_csi_remove,
+ .id_table = imx_csi_ids,
+ .driver = {
+ .name = "imx-ipuv3-csi",
+ },
+};
+module_platform_driver(imx_csi_driver);
+
+MODULE_DESCRIPTION("i.MX CSI subdev driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-csi");
diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
new file mode 100644
index 000000000000..48cbc7716758
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -0,0 +1,667 @@
+/*
+ * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct imx_media_dev, subdev_notifier);
+}
+
+/*
+ * Find a subdev by device node or device name. This is called during
+ * driver load to form the async subdev list and bind them.
+ */
+struct imx_media_subdev *
+imx_media_find_async_subdev(struct imx_media_dev *imxmd,
+ struct device_node *np,
+ const char *devname)
+{
+ struct fwnode_handle *fwnode = np ? of_fwnode_handle(np) : NULL;
+ struct imx_media_subdev *imxsd;
+ int i;
+
+ for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
+ imxsd = &imxmd->subdev[i];
+ switch (imxsd->asd.match_type) {
+ case V4L2_ASYNC_MATCH_FWNODE:
+ if (fwnode && imxsd->asd.match.fwnode.fwnode == fwnode)
+ return imxsd;
+ break;
+ case V4L2_ASYNC_MATCH_DEVNAME:
+ if (devname &&
+ !strcmp(imxsd->asd.match.device_name.name, devname))
+ return imxsd;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Adds a subdev to the async subdev list. If np is non-NULL, adds
+ * the async as a V4L2_ASYNC_MATCH_FWNODE match type, otherwise as
+ * a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name of the
+ * given platform_device. This is called during driver load when
+ * forming the async subdev list.
+ */
+struct imx_media_subdev *
+imx_media_add_async_subdev(struct imx_media_dev *imxmd,
+ struct device_node *np,
+ struct platform_device *pdev)
+{
+ struct imx_media_subdev *imxsd;
+ struct v4l2_async_subdev *asd;
+ const char *devname = NULL;
+ int sd_idx;
+
+ mutex_lock(&imxmd->mutex);
+
+ if (pdev)
+ devname = dev_name(&pdev->dev);
+
+ /* return NULL if this subdev already added */
+ if (imx_media_find_async_subdev(imxmd, np, devname)) {
+ dev_dbg(imxmd->md.dev, "%s: already added %s\n",
+ __func__, np ? np->name : devname);
+ imxsd = NULL;
+ goto out;
+ }
+
+ sd_idx = imxmd->subdev_notifier.num_subdevs;
+ if (sd_idx >= IMX_MEDIA_MAX_SUBDEVS) {
+ dev_err(imxmd->md.dev, "%s: too many subdevs! can't add %s\n",
+ __func__, np ? np->name : devname);
+ imxsd = ERR_PTR(-ENOSPC);
+ goto out;
+ }
+
+ imxsd = &imxmd->subdev[sd_idx];
+
+ asd = &imxsd->asd;
+ if (np) {
+ asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ asd->match.fwnode.fwnode = of_fwnode_handle(np);
+ } else {
+ asd->match_type = V4L2_ASYNC_MATCH_DEVNAME;
+ strncpy(imxsd->devname, devname, sizeof(imxsd->devname));
+ asd->match.device_name.name = imxsd->devname;
+ imxsd->pdev = pdev;
+ }
+
+ imxmd->async_ptrs[sd_idx] = asd;
+ imxmd->subdev_notifier.num_subdevs++;
+
+ dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n",
+ __func__, np ? np->name : devname, np ? "FWNODE" : "DEVNAME");
+
+out:
+ mutex_unlock(&imxmd->mutex);
+ return imxsd;
+}
+
+/*
+ * Adds an imx-media link to a subdev pad's link list. This is called
+ * during driver load when forming the links between subdevs.
+ *
+ * @pad: the local pad
+ * @remote_node: the device node of the remote subdev
+ * @remote_devname: the device name of the remote subdev
+ * @local_pad: local pad index
+ * @remote_pad: remote pad index
+ */
+int imx_media_add_pad_link(struct imx_media_dev *imxmd,
+ struct imx_media_pad *pad,
+ struct device_node *remote_node,
+ const char *remote_devname,
+ int local_pad, int remote_pad)
+{
+ struct imx_media_link *link;
+ int link_idx, ret = 0;
+
+ mutex_lock(&imxmd->mutex);
+
+ link_idx = pad->num_links;
+ if (link_idx >= IMX_MEDIA_MAX_LINKS) {
+ dev_err(imxmd->md.dev, "%s: too many links!\n", __func__);
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ link = &pad->link[link_idx];
+
+ link->remote_sd_node = remote_node;
+ if (remote_devname)
+ strncpy(link->remote_devname, remote_devname,
+ sizeof(link->remote_devname));
+
+ link->local_pad = local_pad;
+ link->remote_pad = remote_pad;
+
+ pad->num_links++;
+out:
+ mutex_unlock(&imxmd->mutex);
+ return ret;
+}
+
+/*
+ * get IPU from this CSI and add it to the list of IPUs
+ * the media driver will control.
+ */
+static int imx_media_get_ipu(struct imx_media_dev *imxmd,
+ struct v4l2_subdev *csi_sd)
+{
+ struct ipu_soc *ipu;
+ int ipu_id;
+
+ ipu = dev_get_drvdata(csi_sd->dev->parent);
+ if (!ipu) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "CSI %s has no parent IPU!\n", csi_sd->name);
+ return -ENODEV;
+ }
+
+ ipu_id = ipu_get_num(ipu);
+ if (ipu_id > 1) {
+ v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
+ return -ENODEV;
+ }
+
+ if (!imxmd->ipu[ipu_id])
+ imxmd->ipu[ipu_id] = ipu;
+
+ return 0;
+}
+
+/* async subdev bound notifier */
+static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct imx_media_dev *imxmd = notifier2dev(notifier);
+ struct device_node *np = to_of_node(sd->fwnode);
+ struct imx_media_subdev *imxsd;
+ int ret = 0;
+
+ mutex_lock(&imxmd->mutex);
+
+ imxsd = imx_media_find_async_subdev(imxmd, np, dev_name(sd->dev));
+ if (!imxsd) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) {
+ ret = imx_media_get_ipu(imxmd, sd);
+ if (ret)
+ goto out_unlock;
+ } else if (sd->entity.function == MEDIA_ENT_F_VID_MUX) {
+ /* this is a video mux */
+ sd->grp_id = IMX_MEDIA_GRP_ID_VIDMUX;
+ } else if (imxsd->num_sink_pads == 0) {
+ /*
+ * this is an original source of video frames, it
+ * could be a camera sensor, an analog decoder, or
+ * a bridge device (HDMI -> MIPI CSI-2 for example).
+ * This group ID is used to locate the entity that
+ * is the original source of video in a pipeline.
+ */
+ sd->grp_id = IMX_MEDIA_GRP_ID_SENSOR;
+ }
+
+ /* attach the subdev */
+ imxsd->sd = sd;
+out:
+ if (ret)
+ v4l2_warn(&imxmd->v4l2_dev,
+ "Received unknown subdev %s\n", sd->name);
+ else
+ v4l2_info(&imxmd->v4l2_dev,
+ "Registered subdev %s\n", sd->name);
+
+out_unlock:
+ mutex_unlock(&imxmd->mutex);
+ return ret;
+}
+
+/*
+ * Create a single source->sink media link given a subdev and a single
+ * link from one of its source pads. Called after all subdevs have
+ * registered.
+ */
+static int imx_media_create_link(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *src,
+ struct imx_media_link *link)
+{
+ struct imx_media_subdev *sink;
+ u16 source_pad, sink_pad;
+ int ret;
+
+ sink = imx_media_find_async_subdev(imxmd, link->remote_sd_node,
+ link->remote_devname);
+ if (!sink) {
+ v4l2_warn(&imxmd->v4l2_dev, "%s: no sink for %s:%d\n",
+ __func__, src->sd->name, link->local_pad);
+ return 0;
+ }
+
+ source_pad = link->local_pad;
+ sink_pad = link->remote_pad;
+
+ v4l2_info(&imxmd->v4l2_dev, "%s: %s:%d -> %s:%d\n", __func__,
+ src->sd->name, source_pad, sink->sd->name, sink_pad);
+
+ ret = media_create_pad_link(&src->sd->entity, source_pad,
+ &sink->sd->entity, sink_pad, 0);
+ if (ret)
+ v4l2_err(&imxmd->v4l2_dev,
+ "create_pad_link failed: %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * create the media links from all imx-media pads and their links.
+ * Called after all subdevs have registered.
+ */
+static int imx_media_create_links(struct imx_media_dev *imxmd)
+{
+ struct imx_media_subdev *imxsd;
+ struct imx_media_link *link;
+ struct imx_media_pad *pad;
+ int num_pads, i, j, k;
+ int ret = 0;
+
+ for (i = 0; i < imxmd->num_subdevs; i++) {
+ imxsd = &imxmd->subdev[i];
+ num_pads = imxsd->num_sink_pads + imxsd->num_src_pads;
+
+ for (j = 0; j < num_pads; j++) {
+ pad = &imxsd->pad[j];
+
+ /* only create the source->sink links */
+ if (!(pad->pad.flags & MEDIA_PAD_FL_SOURCE))
+ continue;
+
+ for (k = 0; k < pad->num_links; k++) {
+ link = &pad->link[k];
+
+ ret = imx_media_create_link(imxmd, imxsd, link);
+ if (ret)
+ goto out;
+ }
+ }
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * adds given video device to given imx-media source pad vdev list.
+ * Continues upstream from the pad entity's sink pads.
+ */
+static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd,
+ struct imx_media_video_dev *vdev,
+ struct media_pad *srcpad)
+{
+ struct media_entity *entity = srcpad->entity;
+ struct imx_media_subdev *imxsd;
+ struct imx_media_pad *imxpad;
+ struct media_link *link;
+ struct v4l2_subdev *sd;
+ int i, vdev_idx, ret;
+
+ /* skip this entity if not a v4l2_subdev */
+ if (!is_media_entity_v4l2_subdev(entity))
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+ imxsd = imx_media_find_subdev_by_sd(imxmd, sd);
+ if (IS_ERR(imxsd))
+ return PTR_ERR(imxsd);
+
+ imxpad = &imxsd->pad[srcpad->index];
+ vdev_idx = imxpad->num_vdevs;
+
+ /* just return if we've been here before */
+ for (i = 0; i < vdev_idx; i++)
+ if (vdev == imxpad->vdev[i])
+ return 0;
+
+ if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) {
+ dev_err(imxmd->md.dev, "can't add %s to pad %s:%u\n",
+ vdev->vfd->entity.name, entity->name, srcpad->index);
+ return -ENOSPC;
+ }
+
+ dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n",
+ vdev->vfd->entity.name, entity->name, srcpad->index);
+ imxpad->vdev[vdev_idx] = vdev;
+ imxpad->num_vdevs++;
+
+ /* move upstream from this entity's sink pads */
+ for (i = 0; i < entity->num_pads; i++) {
+ struct media_pad *pad = &entity->pads[i];
+
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ list_for_each_entry(link, &entity->links, list) {
+ if (link->sink != pad)
+ continue;
+ ret = imx_media_add_vdev_to_pad(imxmd, vdev,
+ link->source);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* form the vdev lists in all imx-media source pads */
+static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd)
+{
+ struct imx_media_video_dev *vdev;
+ struct media_link *link;
+ int i, ret;
+
+ for (i = 0; i < imxmd->num_vdevs; i++) {
+ vdev = imxmd->vdev[i];
+ link = list_first_entry(&vdev->vfd->entity.links,
+ struct media_link, list);
+ ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/* async subdev complete notifier */
+static int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
+{
+ struct imx_media_dev *imxmd = notifier2dev(notifier);
+ int i, ret;
+
+ mutex_lock(&imxmd->mutex);
+
+ /* make sure all subdevs were bound */
+ for (i = 0; i < imxmd->num_subdevs; i++) {
+ if (!imxmd->subdev[i].sd) {
+ v4l2_err(&imxmd->v4l2_dev, "unbound subdev!\n");
+ ret = -ENODEV;
+ goto unlock;
+ }
+ }
+
+ ret = imx_media_create_links(imxmd);
+ if (ret)
+ goto unlock;
+
+ ret = imx_media_create_pad_vdev_lists(imxmd);
+ if (ret)
+ goto unlock;
+
+ ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev);
+unlock:
+ mutex_unlock(&imxmd->mutex);
+ if (ret)
+ return ret;
+
+ return media_device_register(&imxmd->md);
+}
+
+/*
+ * adds controls to a video device from an entity subdevice.
+ * Continues upstream from the entity's sink pads.
+ */
+static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
+ struct video_device *vfd,
+ struct media_entity *entity)
+{
+ int i, ret = 0;
+
+ if (is_media_entity_v4l2_subdev(entity)) {
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+
+ dev_dbg(imxmd->md.dev,
+ "adding controls to %s from %s\n",
+ vfd->entity.name, sd->entity.name);
+
+ ret = v4l2_ctrl_add_handler(vfd->ctrl_handler,
+ sd->ctrl_handler,
+ NULL);
+ if (ret)
+ return ret;
+ }
+
+ /* move upstream */
+ for (i = 0; i < entity->num_pads; i++) {
+ struct media_pad *pad, *spad = &entity->pads[i];
+
+ if (!(spad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ pad = media_entity_remote_pad(spad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ continue;
+
+ ret = imx_media_inherit_controls(imxmd, vfd, pad->entity);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int imx_media_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ struct media_entity *source = link->source->entity;
+ struct imx_media_subdev *imxsd;
+ struct imx_media_pad *imxpad;
+ struct imx_media_dev *imxmd;
+ struct video_device *vfd;
+ struct v4l2_subdev *sd;
+ int i, pad_idx, ret;
+
+ ret = v4l2_pipeline_link_notify(link, flags, notification);
+ if (ret)
+ return ret;
+
+ /* don't bother if source is not a subdev */
+ if (!is_media_entity_v4l2_subdev(source))
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(source);
+ pad_idx = link->source->index;
+
+ imxmd = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ imxsd = imx_media_find_subdev_by_sd(imxmd, sd);
+ if (IS_ERR(imxsd))
+ return PTR_ERR(imxsd);
+ imxpad = &imxsd->pad[pad_idx];
+
+ /*
+ * Before disabling a link, reset controls for all video
+ * devices reachable from this link.
+ *
+ * After enabling a link, refresh controls for all video
+ * devices reachable from this link.
+ */
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
+ !(flags & MEDIA_LNK_FL_ENABLED)) {
+ for (i = 0; i < imxpad->num_vdevs; i++) {
+ vfd = imxpad->vdev[i]->vfd;
+ dev_dbg(imxmd->md.dev,
+ "reset controls for %s\n",
+ vfd->entity.name);
+ v4l2_ctrl_handler_free(vfd->ctrl_handler);
+ v4l2_ctrl_handler_init(vfd->ctrl_handler, 0);
+ }
+ } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (link->flags & MEDIA_LNK_FL_ENABLED)) {
+ for (i = 0; i < imxpad->num_vdevs; i++) {
+ vfd = imxpad->vdev[i]->vfd;
+ dev_dbg(imxmd->md.dev,
+ "refresh controls for %s\n",
+ vfd->entity.name);
+ ret = imx_media_inherit_controls(imxmd, vfd,
+ &vfd->entity);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct media_device_ops imx_media_md_ops = {
+ .link_notify = imx_media_link_notify,
+};
+
+static int imx_media_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct imx_media_subdev *csi[4] = {0};
+ struct imx_media_dev *imxmd;
+ int ret;
+
+ imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL);
+ if (!imxmd)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, imxmd);
+
+ strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model));
+ imxmd->md.ops = &imx_media_md_ops;
+ imxmd->md.dev = dev;
+
+ mutex_init(&imxmd->mutex);
+
+ imxmd->v4l2_dev.mdev = &imxmd->md;
+ strlcpy(imxmd->v4l2_dev.name, "imx-media",
+ sizeof(imxmd->v4l2_dev.name));
+
+ media_device_init(&imxmd->md);
+
+ ret = v4l2_device_register(dev, &imxmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "Failed to register v4l2_device: %d\n", ret);
+ goto cleanup;
+ }
+
+ dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd);
+
+ ret = imx_media_of_parse(imxmd, &csi, node);
+ if (ret) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "imx_media_of_parse failed with %d\n", ret);
+ goto unreg_dev;
+ }
+
+ ret = imx_media_add_internal_subdevs(imxmd, csi);
+ if (ret) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "add_internal_subdevs failed with %d\n", ret);
+ goto unreg_dev;
+ }
+
+ /* no subdevs? just bail */
+ imxmd->num_subdevs = imxmd->subdev_notifier.num_subdevs;
+ if (imxmd->num_subdevs == 0) {
+ ret = -ENODEV;
+ goto unreg_dev;
+ }
+
+ /* prepare the async subdev notifier and register it */
+ imxmd->subdev_notifier.subdevs = imxmd->async_ptrs;
+ imxmd->subdev_notifier.bound = imx_media_subdev_bound;
+ imxmd->subdev_notifier.complete = imx_media_probe_complete;
+ ret = v4l2_async_notifier_register(&imxmd->v4l2_dev,
+ &imxmd->subdev_notifier);
+ if (ret) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "v4l2_async_notifier_register failed with %d\n", ret);
+ goto del_int;
+ }
+
+ return 0;
+
+del_int:
+ imx_media_remove_internal_subdevs(imxmd);
+unreg_dev:
+ v4l2_device_unregister(&imxmd->v4l2_dev);
+cleanup:
+ media_device_cleanup(&imxmd->md);
+ return ret;
+}
+
+static int imx_media_remove(struct platform_device *pdev)
+{
+ struct imx_media_dev *imxmd =
+ (struct imx_media_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n");
+
+ v4l2_async_notifier_unregister(&imxmd->subdev_notifier);
+ imx_media_remove_internal_subdevs(imxmd);
+ v4l2_device_unregister(&imxmd->v4l2_dev);
+ media_device_unregister(&imxmd->md);
+ media_device_cleanup(&imxmd->md);
+
+ return 0;
+}
+
+static const struct of_device_id imx_media_dt_ids[] = {
+ { .compatible = "fsl,imx-capture-subsystem" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_media_dt_ids);
+
+static struct platform_driver imx_media_pdrv = {
+ .probe = imx_media_probe,
+ .remove = imx_media_remove,
+ .driver = {
+ .name = "imx-media",
+ .of_match_table = imx_media_dt_ids,
+ },
+};
+
+module_platform_driver(imx_media_pdrv);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c
new file mode 100644
index 000000000000..47275ef803f3
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-fim.c
@@ -0,0 +1,494 @@
+/*
+ * Frame Interval Monitor.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+enum {
+ FIM_CL_ENABLE = 0,
+ FIM_CL_NUM,
+ FIM_CL_TOLERANCE_MIN,
+ FIM_CL_TOLERANCE_MAX,
+ FIM_CL_NUM_SKIP,
+ FIM_NUM_CONTROLS,
+};
+
+enum {
+ FIM_CL_ICAP_EDGE = 0,
+ FIM_CL_ICAP_CHANNEL,
+ FIM_NUM_ICAP_CONTROLS,
+};
+
+#define FIM_CL_ENABLE_DEF 0 /* FIM disabled by default */
+#define FIM_CL_NUM_DEF 8 /* average 8 frames */
+#define FIM_CL_NUM_SKIP_DEF 2 /* skip 2 frames after restart */
+#define FIM_CL_TOLERANCE_MIN_DEF 50 /* usec */
+#define FIM_CL_TOLERANCE_MAX_DEF 0 /* no max tolerance (unbounded) */
+
+struct imx_media_fim {
+ struct imx_media_dev *md;
+
+ /* the owning subdev of this fim instance */
+ struct v4l2_subdev *sd;
+
+ /* FIM's control handler */
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* control clusters */
+ struct v4l2_ctrl *ctrl[FIM_NUM_CONTROLS];
+ struct v4l2_ctrl *icap_ctrl[FIM_NUM_ICAP_CONTROLS];
+
+ spinlock_t lock; /* protect control values */
+
+ /* current control values */
+ bool enabled;
+ int num_avg;
+ int num_skip;
+ unsigned long tolerance_min; /* usec */
+ unsigned long tolerance_max; /* usec */
+ /* input capture method of measuring FI */
+ int icap_channel;
+ int icap_flags;
+
+ int counter;
+ struct timespec last_ts;
+ unsigned long sum; /* usec */
+ unsigned long nominal; /* usec */
+
+ struct completion icap_first_event;
+ bool stream_on;
+};
+
+#define icap_enabled(fim) ((fim)->icap_flags != IRQ_TYPE_NONE)
+
+static void update_fim_nominal(struct imx_media_fim *fim,
+ const struct v4l2_fract *fi)
+{
+ if (fi->denominator == 0) {
+ dev_dbg(fim->sd->dev, "no frame interval, FIM disabled\n");
+ fim->enabled = false;
+ return;
+ }
+
+ fim->nominal = DIV_ROUND_CLOSEST_ULL(1000000ULL * (u64)fi->numerator,
+ fi->denominator);
+
+ dev_dbg(fim->sd->dev, "FI=%lu usec\n", fim->nominal);
+}
+
+static void reset_fim(struct imx_media_fim *fim, bool curval)
+{
+ struct v4l2_ctrl *icap_chan = fim->icap_ctrl[FIM_CL_ICAP_CHANNEL];
+ struct v4l2_ctrl *icap_edge = fim->icap_ctrl[FIM_CL_ICAP_EDGE];
+ struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE];
+ struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM];
+ struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP];
+ struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN];
+ struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX];
+
+ if (curval) {
+ fim->enabled = en->cur.val;
+ fim->icap_flags = icap_edge->cur.val;
+ fim->icap_channel = icap_chan->cur.val;
+ fim->num_avg = num->cur.val;
+ fim->num_skip = skip->cur.val;
+ fim->tolerance_min = tol_min->cur.val;
+ fim->tolerance_max = tol_max->cur.val;
+ } else {
+ fim->enabled = en->val;
+ fim->icap_flags = icap_edge->val;
+ fim->icap_channel = icap_chan->val;
+ fim->num_avg = num->val;
+ fim->num_skip = skip->val;
+ fim->tolerance_min = tol_min->val;
+ fim->tolerance_max = tol_max->val;
+ }
+
+ /* disable tolerance range if max <= min */
+ if (fim->tolerance_max <= fim->tolerance_min)
+ fim->tolerance_max = 0;
+
+ /* num_skip must be >= 1 if input capture not used */
+ if (!icap_enabled(fim))
+ fim->num_skip = max_t(int, fim->num_skip, 1);
+
+ fim->counter = -fim->num_skip;
+ fim->sum = 0;
+}
+
+static void send_fim_event(struct imx_media_fim *fim, unsigned long error)
+{
+ static const struct v4l2_event ev = {
+ .type = V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR,
+ };
+
+ v4l2_subdev_notify_event(fim->sd, &ev);
+}
+
+/*
+ * Monitor an averaged frame interval. If the average deviates too much
+ * from the nominal frame rate, send the frame interval error event. The
+ * frame intervals are averaged in order to quiet noise from
+ * (presumably random) interrupt latency.
+ */
+static void frame_interval_monitor(struct imx_media_fim *fim,
+ struct timespec *ts)
+{
+ unsigned long interval, error, error_avg;
+ bool send_event = false;
+ struct timespec diff;
+
+ if (!fim->enabled || ++fim->counter <= 0)
+ goto out_update_ts;
+
+ diff = timespec_sub(*ts, fim->last_ts);
+ interval = diff.tv_sec * 1000 * 1000 + diff.tv_nsec / 1000;
+ error = abs(interval - fim->nominal);
+
+ if (fim->tolerance_max && error >= fim->tolerance_max) {
+ dev_dbg(fim->sd->dev,
+ "FIM: %lu ignored, out of tolerance bounds\n",
+ error);
+ fim->counter--;
+ goto out_update_ts;
+ }
+
+ fim->sum += error;
+
+ if (fim->counter == fim->num_avg) {
+ error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg);
+
+ if (error_avg > fim->tolerance_min)
+ send_event = true;
+
+ dev_dbg(fim->sd->dev, "FIM: error: %lu usec%s\n",
+ error_avg, send_event ? " (!!!)" : "");
+
+ fim->counter = 0;
+ fim->sum = 0;
+ }
+
+out_update_ts:
+ fim->last_ts = *ts;
+ if (send_event)
+ send_fim_event(fim, error_avg);
+}
+
+#ifdef CONFIG_IMX_GPT_ICAP
+/*
+ * Input Capture method of measuring frame intervals. Not subject
+ * to interrupt latency.
+ */
+static void fim_input_capture_handler(int channel, void *dev_id,
+ struct timespec *ts)
+{
+ struct imx_media_fim *fim = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fim->lock, flags);
+
+ frame_interval_monitor(fim, ts);
+
+ if (!completion_done(&fim->icap_first_event))
+ complete(&fim->icap_first_event);
+
+ spin_unlock_irqrestore(&fim->lock, flags);
+}
+
+static int fim_request_input_capture(struct imx_media_fim *fim)
+{
+ init_completion(&fim->icap_first_event);
+
+ return mxc_request_input_capture(fim->icap_channel,
+ fim_input_capture_handler,
+ fim->icap_flags, fim);
+}
+
+static void fim_free_input_capture(struct imx_media_fim *fim)
+{
+ mxc_free_input_capture(fim->icap_channel, fim);
+}
+
+#else /* CONFIG_IMX_GPT_ICAP */
+
+static int fim_request_input_capture(struct imx_media_fim *fim)
+{
+ return 0;
+}
+
+static void fim_free_input_capture(struct imx_media_fim *fim)
+{
+}
+
+#endif /* CONFIG_IMX_GPT_ICAP */
+
+/*
+ * In case we are monitoring the first frame interval after streamon
+ * (when fim->num_skip = 0), we need a valid fim->last_ts before we
+ * can begin. This only applies to the input capture method. It is not
+ * possible to accurately measure the first FI after streamon using the
+ * EOF method, so fim->num_skip minimum is set to 1 in that case, so this
+ * function is a noop when the EOF method is used.
+ */
+static void fim_acquire_first_ts(struct imx_media_fim *fim)
+{
+ unsigned long ret;
+
+ if (!fim->enabled || fim->num_skip > 0)
+ return;
+
+ ret = wait_for_completion_timeout(
+ &fim->icap_first_event,
+ msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
+ if (ret == 0)
+ v4l2_warn(fim->sd, "wait first icap event timeout\n");
+}
+
+/* FIM Controls */
+static int fim_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx_media_fim *fim = container_of(ctrl->handler,
+ struct imx_media_fim,
+ ctrl_handler);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&fim->lock, flags);
+
+ switch (ctrl->id) {
+ case V4L2_CID_IMX_FIM_ENABLE:
+ break;
+ case V4L2_CID_IMX_FIM_ICAP_EDGE:
+ if (fim->stream_on)
+ ret = -EBUSY;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ reset_fim(fim, false);
+
+ spin_unlock_irqrestore(&fim->lock, flags);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops fim_ctrl_ops = {
+ .s_ctrl = fim_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config fim_ctrl[] = {
+ [FIM_CL_ENABLE] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_ENABLE,
+ .name = "FIM Enable",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .def = FIM_CL_ENABLE_DEF,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ },
+ [FIM_CL_NUM] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_NUM,
+ .name = "FIM Num Average",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = FIM_CL_NUM_DEF,
+ .min = 1, /* no averaging */
+ .max = 64, /* average 64 frames */
+ .step = 1,
+ },
+ [FIM_CL_TOLERANCE_MIN] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+ .name = "FIM Tolerance Min",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = FIM_CL_TOLERANCE_MIN_DEF,
+ .min = 2,
+ .max = 200,
+ .step = 1,
+ },
+ [FIM_CL_TOLERANCE_MAX] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+ .name = "FIM Tolerance Max",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = FIM_CL_TOLERANCE_MAX_DEF,
+ .min = 0,
+ .max = 500,
+ .step = 1,
+ },
+ [FIM_CL_NUM_SKIP] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_NUM_SKIP,
+ .name = "FIM Num Skip",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = FIM_CL_NUM_SKIP_DEF,
+ .min = 0, /* skip no frames */
+ .max = 256, /* skip 256 frames */
+ .step = 1,
+ },
+};
+
+static const struct v4l2_ctrl_config fim_icap_ctrl[] = {
+ [FIM_CL_ICAP_EDGE] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_ICAP_EDGE,
+ .name = "FIM Input Capture Edge",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = IRQ_TYPE_NONE, /* input capture disabled by default */
+ .min = IRQ_TYPE_NONE,
+ .max = IRQ_TYPE_EDGE_BOTH,
+ .step = 1,
+ },
+ [FIM_CL_ICAP_CHANNEL] = {
+ .ops = &fim_ctrl_ops,
+ .id = V4L2_CID_IMX_FIM_ICAP_CHANNEL,
+ .name = "FIM Input Capture Channel",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 0,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ },
+};
+
+static int init_fim_controls(struct imx_media_fim *fim)
+{
+ struct v4l2_ctrl_handler *hdlr = &fim->ctrl_handler;
+ int i, ret;
+
+ v4l2_ctrl_handler_init(hdlr, FIM_NUM_CONTROLS + FIM_NUM_ICAP_CONTROLS);
+
+ for (i = 0; i < FIM_NUM_CONTROLS; i++)
+ fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr,
+ &fim_ctrl[i],
+ NULL);
+ for (i = 0; i < FIM_NUM_ICAP_CONTROLS; i++)
+ fim->icap_ctrl[i] = v4l2_ctrl_new_custom(hdlr,
+ &fim_icap_ctrl[i],
+ NULL);
+ if (hdlr->error) {
+ ret = hdlr->error;
+ goto err_free;
+ }
+
+ v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl);
+ v4l2_ctrl_cluster(FIM_NUM_ICAP_CONTROLS, fim->icap_ctrl);
+
+ return 0;
+err_free:
+ v4l2_ctrl_handler_free(hdlr);
+ return ret;
+}
+
+/*
+ * Monitor frame intervals via EOF interrupt. This method is
+ * subject to uncertainty errors introduced by interrupt latency.
+ *
+ * This is a noop if the Input Capture method is being used, since
+ * the frame_interval_monitor() is called by the input capture event
+ * callback handler in that case.
+ */
+void imx_media_fim_eof_monitor(struct imx_media_fim *fim, struct timespec *ts)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fim->lock, flags);
+
+ if (!icap_enabled(fim))
+ frame_interval_monitor(fim, ts);
+
+ spin_unlock_irqrestore(&fim->lock, flags);
+}
+EXPORT_SYMBOL_GPL(imx_media_fim_eof_monitor);
+
+/* Called by the subdev in its s_stream callback */
+int imx_media_fim_set_stream(struct imx_media_fim *fim,
+ const struct v4l2_fract *fi,
+ bool on)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ v4l2_ctrl_lock(fim->ctrl[FIM_CL_ENABLE]);
+
+ if (fim->stream_on == on)
+ goto out;
+
+ if (on) {
+ spin_lock_irqsave(&fim->lock, flags);
+ reset_fim(fim, true);
+ update_fim_nominal(fim, fi);
+ spin_unlock_irqrestore(&fim->lock, flags);
+
+ if (icap_enabled(fim)) {
+ ret = fim_request_input_capture(fim);
+ if (ret)
+ goto out;
+ fim_acquire_first_ts(fim);
+ }
+ } else {
+ if (icap_enabled(fim))
+ fim_free_input_capture(fim);
+ }
+
+ fim->stream_on = on;
+out:
+ v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_fim_set_stream);
+
+int imx_media_fim_add_controls(struct imx_media_fim *fim)
+{
+ /* add the FIM controls to the calling subdev ctrl handler */
+ return v4l2_ctrl_add_handler(fim->sd->ctrl_handler,
+ &fim->ctrl_handler, NULL);
+}
+EXPORT_SYMBOL_GPL(imx_media_fim_add_controls);
+
+/* Called by the subdev in its subdev registered callback */
+struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd)
+{
+ struct imx_media_fim *fim;
+ int ret;
+
+ fim = devm_kzalloc(sd->dev, sizeof(*fim), GFP_KERNEL);
+ if (!fim)
+ return ERR_PTR(-ENOMEM);
+
+ /* get media device */
+ fim->md = dev_get_drvdata(sd->v4l2_dev->dev);
+ fim->sd = sd;
+
+ spin_lock_init(&fim->lock);
+
+ ret = init_fim_controls(fim);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return fim;
+}
+EXPORT_SYMBOL_GPL(imx_media_fim_init);
+
+void imx_media_fim_free(struct imx_media_fim *fim)
+{
+ v4l2_ctrl_handler_free(&fim->ctrl_handler);
+}
+EXPORT_SYMBOL_GPL(imx_media_fim_free);
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c
new file mode 100644
index 000000000000..cdfbf40dfcbe
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-internal-sd.c
@@ -0,0 +1,349 @@
+/*
+ * Media driver for Freescale i.MX5/6 SOC
+ *
+ * Adds the internal subdevices and the media links between them.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/platform_device.h>
+#include "imx-media.h"
+
+enum isd_enum {
+ isd_csi0 = 0,
+ isd_csi1,
+ isd_vdic,
+ isd_ic_prp,
+ isd_ic_prpenc,
+ isd_ic_prpvf,
+ num_isd,
+};
+
+static const struct internal_subdev_id {
+ enum isd_enum index;
+ const char *name;
+ u32 grp_id;
+} isd_id[num_isd] = {
+ [isd_csi0] = {
+ .index = isd_csi0,
+ .grp_id = IMX_MEDIA_GRP_ID_CSI0,
+ .name = "imx-ipuv3-csi",
+ },
+ [isd_csi1] = {
+ .index = isd_csi1,
+ .grp_id = IMX_MEDIA_GRP_ID_CSI1,
+ .name = "imx-ipuv3-csi",
+ },
+ [isd_vdic] = {
+ .index = isd_vdic,
+ .grp_id = IMX_MEDIA_GRP_ID_VDIC,
+ .name = "imx-ipuv3-vdic",
+ },
+ [isd_ic_prp] = {
+ .index = isd_ic_prp,
+ .grp_id = IMX_MEDIA_GRP_ID_IC_PRP,
+ .name = "imx-ipuv3-ic",
+ },
+ [isd_ic_prpenc] = {
+ .index = isd_ic_prpenc,
+ .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC,
+ .name = "imx-ipuv3-ic",
+ },
+ [isd_ic_prpvf] = {
+ .index = isd_ic_prpvf,
+ .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF,
+ .name = "imx-ipuv3-ic",
+ },
+};
+
+struct internal_link {
+ const struct internal_subdev_id *remote_id;
+ int remote_pad;
+};
+
+struct internal_pad {
+ bool devnode; /* does this pad link to a device node */
+ struct internal_link link[IMX_MEDIA_MAX_LINKS];
+};
+
+static const struct internal_subdev {
+ const struct internal_subdev_id *id;
+ struct internal_pad pad[IMX_MEDIA_MAX_PADS];
+ int num_sink_pads;
+ int num_src_pads;
+} internal_subdev[num_isd] = {
+ [isd_csi0] = {
+ .id = &isd_id[isd_csi0],
+ .num_sink_pads = CSI_NUM_SINK_PADS,
+ .num_src_pads = CSI_NUM_SRC_PADS,
+ .pad[CSI_SRC_PAD_DIRECT] = {
+ .link = {
+ {
+ .remote_id = &isd_id[isd_ic_prp],
+ .remote_pad = PRP_SINK_PAD,
+ }, {
+ .remote_id = &isd_id[isd_vdic],
+ .remote_pad = VDIC_SINK_PAD_DIRECT,
+ },
+ },
+ },
+ .pad[CSI_SRC_PAD_IDMAC] = {
+ .devnode = true,
+ },
+ },
+
+ [isd_csi1] = {
+ .id = &isd_id[isd_csi1],
+ .num_sink_pads = CSI_NUM_SINK_PADS,
+ .num_src_pads = CSI_NUM_SRC_PADS,
+ .pad[CSI_SRC_PAD_DIRECT] = {
+ .link = {
+ {
+ .remote_id = &isd_id[isd_ic_prp],
+ .remote_pad = PRP_SINK_PAD,
+ }, {
+ .remote_id = &isd_id[isd_vdic],
+ .remote_pad = VDIC_SINK_PAD_DIRECT,
+ },
+ },
+ },
+ .pad[CSI_SRC_PAD_IDMAC] = {
+ .devnode = true,
+ },
+ },
+
+ [isd_vdic] = {
+ .id = &isd_id[isd_vdic],
+ .num_sink_pads = VDIC_NUM_SINK_PADS,
+ .num_src_pads = VDIC_NUM_SRC_PADS,
+ .pad[VDIC_SINK_PAD_IDMAC] = {
+ .devnode = true,
+ },
+ .pad[VDIC_SRC_PAD_DIRECT] = {
+ .link = {
+ {
+ .remote_id = &isd_id[isd_ic_prp],
+ .remote_pad = PRP_SINK_PAD,
+ },
+ },
+ },
+ },
+
+ [isd_ic_prp] = {
+ .id = &isd_id[isd_ic_prp],
+ .num_sink_pads = PRP_NUM_SINK_PADS,
+ .num_src_pads = PRP_NUM_SRC_PADS,
+ .pad[PRP_SRC_PAD_PRPENC] = {
+ .link = {
+ {
+ .remote_id = &isd_id[isd_ic_prpenc],
+ .remote_pad = 0,
+ },
+ },
+ },
+ .pad[PRP_SRC_PAD_PRPVF] = {
+ .link = {
+ {
+ .remote_id = &isd_id[isd_ic_prpvf],
+ .remote_pad = 0,
+ },
+ },
+ },
+ },
+
+ [isd_ic_prpenc] = {
+ .id = &isd_id[isd_ic_prpenc],
+ .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
+ .num_src_pads = PRPENCVF_NUM_SRC_PADS,
+ .pad[PRPENCVF_SRC_PAD] = {
+ .devnode = true,
+ },
+ },
+
+ [isd_ic_prpvf] = {
+ .id = &isd_id[isd_ic_prpvf],
+ .num_sink_pads = PRPENCVF_NUM_SINK_PADS,
+ .num_src_pads = PRPENCVF_NUM_SRC_PADS,
+ .pad[PRPENCVF_SRC_PAD] = {
+ .devnode = true,
+ },
+ },
+};
+
+/* form a device name given a group id and ipu id */
+static inline void isd_id_to_devname(char *devname, int sz,
+ const struct internal_subdev_id *id,
+ int ipu_id)
+{
+ int pdev_id = ipu_id * num_isd + id->index;
+
+ snprintf(devname, sz, "%s.%d", id->name, pdev_id);
+}
+
+/* adds the links from given internal subdev */
+static int add_internal_links(struct imx_media_dev *imxmd,
+ const struct internal_subdev *isd,
+ struct imx_media_subdev *imxsd,
+ int ipu_id)
+{
+ int i, num_pads, ret;
+
+ num_pads = isd->num_sink_pads + isd->num_src_pads;
+
+ for (i = 0; i < num_pads; i++) {
+ const struct internal_pad *intpad = &isd->pad[i];
+ struct imx_media_pad *pad = &imxsd->pad[i];
+ int j;
+
+ /* init the pad flags for this internal subdev */
+ pad->pad.flags = (i < isd->num_sink_pads) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+ /* export devnode pad flag to the subdevs */
+ pad->devnode = intpad->devnode;
+
+ for (j = 0; ; j++) {
+ const struct internal_link *link;
+ char remote_devname[32];
+
+ link = &intpad->link[j];
+
+ if (!link->remote_id)
+ break;
+
+ isd_id_to_devname(remote_devname,
+ sizeof(remote_devname),
+ link->remote_id, ipu_id);
+
+ ret = imx_media_add_pad_link(imxmd, pad,
+ NULL, remote_devname,
+ i, link->remote_pad);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* register an internal subdev as a platform device */
+static struct imx_media_subdev *
+add_internal_subdev(struct imx_media_dev *imxmd,
+ const struct internal_subdev *isd,
+ int ipu_id)
+{
+ struct imx_media_internal_sd_platformdata pdata;
+ struct platform_device_info pdevinfo = {0};
+ struct imx_media_subdev *imxsd;
+ struct platform_device *pdev;
+
+ pdata.grp_id = isd->id->grp_id;
+
+ /* the id of IPU this subdev will control */
+ pdata.ipu_id = ipu_id;
+
+ /* create subdev name */
+ imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
+ pdata.grp_id, ipu_id);
+
+ pdevinfo.name = isd->id->name;
+ pdevinfo.id = ipu_id * num_isd + isd->id->index;
+ pdevinfo.parent = imxmd->md.dev;
+ pdevinfo.data = &pdata;
+ pdevinfo.size_data = sizeof(pdata);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev))
+ return ERR_CAST(pdev);
+
+ imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
+ if (IS_ERR(imxsd))
+ return imxsd;
+
+ imxsd->num_sink_pads = isd->num_sink_pads;
+ imxsd->num_src_pads = isd->num_src_pads;
+
+ return imxsd;
+}
+
+/* adds the internal subdevs in one ipu */
+static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *csi0,
+ struct imx_media_subdev *csi1,
+ int ipu_id)
+{
+ enum isd_enum i;
+ int ret;
+
+ for (i = 0; i < num_isd; i++) {
+ const struct internal_subdev *isd = &internal_subdev[i];
+ struct imx_media_subdev *imxsd;
+
+ /*
+ * the CSIs are represented in the device-tree, so those
+ * devices are added already, and are added to the async
+ * subdev list by of_parse_subdev(), so we are given those
+ * subdevs as csi0 and csi1.
+ */
+ switch (isd->id->grp_id) {
+ case IMX_MEDIA_GRP_ID_CSI0:
+ imxsd = csi0;
+ break;
+ case IMX_MEDIA_GRP_ID_CSI1:
+ imxsd = csi1;
+ break;
+ default:
+ imxsd = add_internal_subdev(imxmd, isd, ipu_id);
+ break;
+ }
+
+ if (IS_ERR(imxsd))
+ return PTR_ERR(imxsd);
+
+ /* add the links from this subdev */
+ if (imxsd) {
+ ret = add_internal_links(imxmd, isd, imxsd, ipu_id);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *csi[4])
+{
+ int ret;
+
+ ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0);
+ if (ret)
+ goto remove;
+
+ ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1);
+ if (ret)
+ goto remove;
+
+ return 0;
+
+remove:
+ imx_media_remove_internal_subdevs(imxmd);
+ return ret;
+}
+
+void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
+{
+ struct imx_media_subdev *imxsd;
+ int i;
+
+ for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) {
+ imxsd = &imxmd->subdev[i];
+ if (!imxsd->pdev)
+ continue;
+ platform_device_unregister(imxsd->pdev);
+ }
+}
diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
new file mode 100644
index 000000000000..b026fe66467c
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -0,0 +1,270 @@
+/*
+ * Media driver for Freescale i.MX5/6 SOC
+ *
+ * Open Firmware parsing.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/of_platform.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/of_graph.h>
+#include <video/imx-ipu-v3.h>
+#include "imx-media.h"
+
+static int of_add_pad_link(struct imx_media_dev *imxmd,
+ struct imx_media_pad *pad,
+ struct device_node *local_sd_node,
+ struct device_node *remote_sd_node,
+ int local_pad, int remote_pad)
+{
+ dev_dbg(imxmd->md.dev, "%s: adding %s:%d -> %s:%d\n", __func__,
+ local_sd_node->name, local_pad,
+ remote_sd_node->name, remote_pad);
+
+ return imx_media_add_pad_link(imxmd, pad, remote_sd_node, NULL,
+ local_pad, remote_pad);
+}
+
+static void of_parse_sensor(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *sensor,
+ struct device_node *sensor_np)
+{
+ struct device_node *endpoint;
+
+ endpoint = of_graph_get_next_endpoint(sensor_np, NULL);
+ if (endpoint) {
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+ &sensor->sensor_ep);
+ of_node_put(endpoint);
+ }
+}
+
+static int of_get_port_count(const struct device_node *np)
+{
+ struct device_node *ports, *child;
+ int num = 0;
+
+ /* check if this node has a ports subnode */
+ ports = of_get_child_by_name(np, "ports");
+ if (ports)
+ np = ports;
+
+ for_each_child_of_node(np, child)
+ if (of_node_cmp(child->name, "port") == 0)
+ num++;
+
+ of_node_put(ports);
+ return num;
+}
+
+/*
+ * find the remote device node and remote port id (remote pad #)
+ * given local endpoint node
+ */
+static void of_get_remote_pad(struct device_node *epnode,
+ struct device_node **remote_node,
+ int *remote_pad)
+{
+ struct device_node *rp, *rpp;
+ struct device_node *remote;
+
+ rp = of_graph_get_remote_port(epnode);
+ rpp = of_graph_get_remote_port_parent(epnode);
+
+ if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) {
+ /* the remote is one of the CSI ports */
+ remote = rp;
+ *remote_pad = 0;
+ of_node_put(rpp);
+ } else {
+ remote = rpp;
+ if (of_property_read_u32(rp, "reg", remote_pad))
+ *remote_pad = 0;
+ of_node_put(rp);
+ }
+
+ if (!of_device_is_available(remote)) {
+ of_node_put(remote);
+ *remote_node = NULL;
+ } else {
+ *remote_node = remote;
+ }
+}
+
+static struct imx_media_subdev *
+of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
+ bool is_csi_port)
+{
+ struct imx_media_subdev *imxsd;
+ int i, num_pads, ret;
+
+ if (!of_device_is_available(sd_np)) {
+ dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__,
+ sd_np->name);
+ return NULL;
+ }
+
+ /* register this subdev with async notifier */
+ imxsd = imx_media_add_async_subdev(imxmd, sd_np, NULL);
+ if (IS_ERR_OR_NULL(imxsd))
+ return imxsd;
+
+ if (is_csi_port) {
+ /*
+ * the ipu-csi has one sink port and two source ports.
+ * The source ports are not represented in the device tree,
+ * but are described by the internal pads and links later.
+ */
+ num_pads = CSI_NUM_PADS;
+ imxsd->num_sink_pads = CSI_NUM_SINK_PADS;
+ } else if (of_device_is_compatible(sd_np, "fsl,imx6-mipi-csi2")) {
+ num_pads = of_get_port_count(sd_np);
+ /* the mipi csi2 receiver has only one sink port */
+ imxsd->num_sink_pads = 1;
+ } else if (of_device_is_compatible(sd_np, "video-mux")) {
+ num_pads = of_get_port_count(sd_np);
+ /* for the video mux, all but the last port are sinks */
+ imxsd->num_sink_pads = num_pads - 1;
+ } else {
+ num_pads = of_get_port_count(sd_np);
+ if (num_pads != 1) {
+ dev_warn(imxmd->md.dev,
+ "%s: unknown device %s with %d ports\n",
+ __func__, sd_np->name, num_pads);
+ return NULL;
+ }
+
+ /*
+ * we got to this node from this single source port,
+ * there are no sink pads.
+ */
+ imxsd->num_sink_pads = 0;
+ }
+
+ if (imxsd->num_sink_pads >= num_pads)
+ return ERR_PTR(-EINVAL);
+
+ imxsd->num_src_pads = num_pads - imxsd->num_sink_pads;
+
+ dev_dbg(imxmd->md.dev, "%s: %s has %d pads (%d sink, %d src)\n",
+ __func__, sd_np->name, num_pads,
+ imxsd->num_sink_pads, imxsd->num_src_pads);
+
+ /*
+ * With no sink, this subdev node is the original source
+ * of video, parse it's media bus for use by the pipeline.
+ */
+ if (imxsd->num_sink_pads == 0)
+ of_parse_sensor(imxmd, imxsd, sd_np);
+
+ for (i = 0; i < num_pads; i++) {
+ struct device_node *epnode = NULL, *port, *remote_np;
+ struct imx_media_subdev *remote_imxsd;
+ struct imx_media_pad *pad;
+ int remote_pad;
+
+ /* init this pad */
+ pad = &imxsd->pad[i];
+ pad->pad.flags = (i < imxsd->num_sink_pads) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ if (is_csi_port)
+ port = (i < imxsd->num_sink_pads) ? sd_np : NULL;
+ else
+ port = of_graph_get_port_by_id(sd_np, i);
+ if (!port)
+ continue;
+
+ for_each_child_of_node(port, epnode) {
+ of_get_remote_pad(epnode, &remote_np, &remote_pad);
+ if (!remote_np)
+ continue;
+
+ ret = of_add_pad_link(imxmd, pad, sd_np, remote_np,
+ i, remote_pad);
+ if (ret) {
+ imxsd = ERR_PTR(ret);
+ break;
+ }
+
+ if (i < imxsd->num_sink_pads) {
+ /* follow sink endpoints upstream */
+ remote_imxsd = of_parse_subdev(imxmd,
+ remote_np,
+ false);
+ if (IS_ERR(remote_imxsd)) {
+ imxsd = remote_imxsd;
+ break;
+ }
+ }
+
+ of_node_put(remote_np);
+ }
+
+ if (port != sd_np)
+ of_node_put(port);
+ if (IS_ERR(imxsd)) {
+ of_node_put(remote_np);
+ of_node_put(epnode);
+ break;
+ }
+ }
+
+ return imxsd;
+}
+
+int imx_media_of_parse(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *(*csi)[4],
+ struct device_node *np)
+{
+ struct imx_media_subdev *lcsi;
+ struct device_node *csi_np;
+ u32 ipu_id, csi_id;
+ int i, ret;
+
+ for (i = 0; ; i++) {
+ csi_np = of_parse_phandle(np, "ports", i);
+ if (!csi_np)
+ break;
+
+ lcsi = of_parse_subdev(imxmd, csi_np, true);
+ if (IS_ERR(lcsi)) {
+ ret = PTR_ERR(lcsi);
+ goto err_put;
+ }
+
+ ret = of_property_read_u32(csi_np, "reg", &csi_id);
+ if (ret) {
+ dev_err(imxmd->md.dev,
+ "%s: csi port missing reg property!\n",
+ __func__);
+ goto err_put;
+ }
+
+ ipu_id = of_alias_get_id(csi_np->parent, "ipu");
+ of_node_put(csi_np);
+
+ if (ipu_id > 1 || csi_id > 1) {
+ dev_err(imxmd->md.dev,
+ "%s: invalid ipu/csi id (%u/%u)\n",
+ __func__, ipu_id, csi_id);
+ return -EINVAL;
+ }
+
+ (*csi)[ipu_id * 2 + csi_id] = lcsi;
+ }
+
+ return 0;
+err_put:
+ of_node_put(csi_np);
+ return ret;
+}
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
new file mode 100644
index 000000000000..59523872a886
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -0,0 +1,896 @@
+/*
+ * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include "imx-media.h"
+
+/*
+ * List of supported pixel formats for the subdevs.
+ *
+ * In all of these tables, the non-mbus formats (with no
+ * mbus codes) must all fall at the end of the table.
+ */
+
+static const struct imx_media_pixfmt yuv_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .codes = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_1X16
+ },
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .codes = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YUYV8_1X16
+ },
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 16,
+ },
+ /***
+ * non-mbus YUV formats start here. NOTE! when adding non-mbus
+ * formats, NUM_NON_MBUS_YUV_FORMATS must be updated below.
+ ***/
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 12,
+ .planar = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 12,
+ .planar = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 16,
+ .planar = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 12,
+ .planar = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 16,
+ .planar = true,
+ },
+};
+
+#define NUM_NON_MBUS_YUV_FORMATS 5
+#define NUM_YUV_FORMATS ARRAY_SIZE(yuv_formats)
+#define NUM_MBUS_YUV_FORMATS (NUM_YUV_FORMATS - NUM_NON_MBUS_YUV_FORMATS)
+
+static const struct imx_media_pixfmt rgb_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .codes = {MEDIA_BUS_FMT_RGB565_2X8_LE},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .codes = {
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_RGB888_2X12_LE
+ },
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ .ipufmt = true,
+ },
+ /*** raw bayer formats start here ***/
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .codes = {MEDIA_BUS_FMT_SBGGR8_1X8},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 8,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .codes = {MEDIA_BUS_FMT_SGBRG8_1X8},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 8,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .codes = {MEDIA_BUS_FMT_SGRBG8_1X8},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 8,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .codes = {MEDIA_BUS_FMT_SRGGB8_1X8},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 8,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SBGGR16_1X16
+ },
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 16,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .codes = {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGBRG16_1X16,
+ },
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 16,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .codes = {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SGRBG16_1X16,
+ },
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 16,
+ .bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .codes = {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ MEDIA_BUS_FMT_SRGGB16_1X16,
+ },
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 16,
+ .bayer = true,
+ },
+ /***
+ * non-mbus RGB formats start here. NOTE! when adding non-mbus
+ * formats, NUM_NON_MBUS_RGB_FORMATS must be updated below.
+ ***/
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ },
+};
+
+#define NUM_NON_MBUS_RGB_FORMATS 2
+#define NUM_RGB_FORMATS ARRAY_SIZE(rgb_formats)
+#define NUM_MBUS_RGB_FORMATS (NUM_RGB_FORMATS - NUM_NON_MBUS_RGB_FORMATS)
+
+static const struct imx_media_pixfmt ipu_yuv_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUV32,
+ .codes = {MEDIA_BUS_FMT_AYUV8_1X32},
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 32,
+ .ipufmt = true,
+ },
+};
+
+#define NUM_IPU_YUV_FORMATS ARRAY_SIZE(ipu_yuv_formats)
+
+static const struct imx_media_pixfmt ipu_rgb_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ .ipufmt = true,
+ },
+};
+
+#define NUM_IPU_RGB_FORMATS ARRAY_SIZE(ipu_rgb_formats)
+
+static void init_mbus_colorimetry(struct v4l2_mbus_framefmt *mbus,
+ const struct imx_media_pixfmt *fmt)
+{
+ mbus->colorspace = (fmt->cs == IPUV3_COLORSPACE_RGB) ?
+ V4L2_COLORSPACE_SRGB : V4L2_COLORSPACE_SMPTE170M;
+ mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
+ mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
+ mbus->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(fmt->cs == IPUV3_COLORSPACE_RGB,
+ mbus->colorspace,
+ mbus->ycbcr_enc);
+}
+
+static const struct imx_media_pixfmt *find_format(u32 fourcc,
+ u32 code,
+ enum codespace_sel cs_sel,
+ bool allow_non_mbus,
+ bool allow_bayer)
+{
+ const struct imx_media_pixfmt *array, *fmt, *ret = NULL;
+ u32 array_size;
+ int i, j;
+
+ switch (cs_sel) {
+ case CS_SEL_YUV:
+ array_size = NUM_YUV_FORMATS;
+ array = yuv_formats;
+ break;
+ case CS_SEL_RGB:
+ array_size = NUM_RGB_FORMATS;
+ array = rgb_formats;
+ break;
+ case CS_SEL_ANY:
+ array_size = NUM_YUV_FORMATS + NUM_RGB_FORMATS;
+ array = yuv_formats;
+ break;
+ default:
+ return NULL;
+ }
+
+ for (i = 0; i < array_size; i++) {
+ if (cs_sel == CS_SEL_ANY && i >= NUM_YUV_FORMATS)
+ fmt = &rgb_formats[i - NUM_YUV_FORMATS];
+ else
+ fmt = &array[i];
+
+ if ((!allow_non_mbus && fmt->codes[0] == 0) ||
+ (!allow_bayer && fmt->bayer))
+ continue;
+
+ if (fourcc && fmt->fourcc == fourcc) {
+ ret = fmt;
+ goto out;
+ }
+
+ for (j = 0; code && fmt->codes[j]; j++) {
+ if (code == fmt->codes[j]) {
+ ret = fmt;
+ goto out;
+ }
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int enum_format(u32 *fourcc, u32 *code, u32 index,
+ enum codespace_sel cs_sel,
+ bool allow_non_mbus,
+ bool allow_bayer)
+{
+ const struct imx_media_pixfmt *fmt;
+ u32 mbus_yuv_sz = NUM_MBUS_YUV_FORMATS;
+ u32 mbus_rgb_sz = NUM_MBUS_RGB_FORMATS;
+ u32 yuv_sz = NUM_YUV_FORMATS;
+ u32 rgb_sz = NUM_RGB_FORMATS;
+
+ switch (cs_sel) {
+ case CS_SEL_YUV:
+ if (index >= yuv_sz ||
+ (!allow_non_mbus && index >= mbus_yuv_sz))
+ return -EINVAL;
+ fmt = &yuv_formats[index];
+ break;
+ case CS_SEL_RGB:
+ if (index >= rgb_sz ||
+ (!allow_non_mbus && index >= mbus_rgb_sz))
+ return -EINVAL;
+ fmt = &rgb_formats[index];
+ if (!allow_bayer && fmt->bayer)
+ return -EINVAL;
+ break;
+ case CS_SEL_ANY:
+ if (!allow_non_mbus) {
+ if (index >= mbus_yuv_sz) {
+ index -= mbus_yuv_sz;
+ if (index >= mbus_rgb_sz)
+ return -EINVAL;
+ fmt = &rgb_formats[index];
+ if (!allow_bayer && fmt->bayer)
+ return -EINVAL;
+ } else {
+ fmt = &yuv_formats[index];
+ }
+ } else {
+ if (index >= yuv_sz + rgb_sz)
+ return -EINVAL;
+ if (index >= yuv_sz) {
+ fmt = &rgb_formats[index - yuv_sz];
+ if (!allow_bayer && fmt->bayer)
+ return -EINVAL;
+ } else {
+ fmt = &yuv_formats[index];
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (fourcc)
+ *fourcc = fmt->fourcc;
+ if (code)
+ *code = fmt->codes[0];
+
+ return 0;
+}
+
+const struct imx_media_pixfmt *
+imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer)
+{
+ return find_format(fourcc, 0, cs_sel, true, allow_bayer);
+}
+EXPORT_SYMBOL_GPL(imx_media_find_format);
+
+int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel)
+{
+ return enum_format(fourcc, NULL, index, cs_sel, true, false);
+}
+EXPORT_SYMBOL_GPL(imx_media_enum_format);
+
+const struct imx_media_pixfmt *
+imx_media_find_mbus_format(u32 code, enum codespace_sel cs_sel,
+ bool allow_bayer)
+{
+ return find_format(0, code, cs_sel, false, allow_bayer);
+}
+EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
+
+int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel,
+ bool allow_bayer)
+{
+ return enum_format(NULL, code, index, cs_sel, false, allow_bayer);
+}
+EXPORT_SYMBOL_GPL(imx_media_enum_mbus_format);
+
+const struct imx_media_pixfmt *
+imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel)
+{
+ const struct imx_media_pixfmt *array, *fmt, *ret = NULL;
+ u32 array_size;
+ int i, j;
+
+ switch (cs_sel) {
+ case CS_SEL_YUV:
+ array_size = NUM_IPU_YUV_FORMATS;
+ array = ipu_yuv_formats;
+ break;
+ case CS_SEL_RGB:
+ array_size = NUM_IPU_RGB_FORMATS;
+ array = ipu_rgb_formats;
+ break;
+ case CS_SEL_ANY:
+ array_size = NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS;
+ array = ipu_yuv_formats;
+ break;
+ default:
+ return NULL;
+ }
+
+ for (i = 0; i < array_size; i++) {
+ if (cs_sel == CS_SEL_ANY && i >= NUM_IPU_YUV_FORMATS)
+ fmt = &ipu_rgb_formats[i - NUM_IPU_YUV_FORMATS];
+ else
+ fmt = &array[i];
+
+ for (j = 0; code && fmt->codes[j]; j++) {
+ if (code == fmt->codes[j]) {
+ ret = fmt;
+ goto out;
+ }
+ }
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_find_ipu_format);
+
+int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel)
+{
+ switch (cs_sel) {
+ case CS_SEL_YUV:
+ if (index >= NUM_IPU_YUV_FORMATS)
+ return -EINVAL;
+ *code = ipu_yuv_formats[index].codes[0];
+ break;
+ case CS_SEL_RGB:
+ if (index >= NUM_IPU_RGB_FORMATS)
+ return -EINVAL;
+ *code = ipu_rgb_formats[index].codes[0];
+ break;
+ case CS_SEL_ANY:
+ if (index >= NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS)
+ return -EINVAL;
+ if (index >= NUM_IPU_YUV_FORMATS) {
+ index -= NUM_IPU_YUV_FORMATS;
+ *code = ipu_rgb_formats[index].codes[0];
+ } else {
+ *code = ipu_yuv_formats[index].codes[0];
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_enum_ipu_format);
+
+int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
+ u32 width, u32 height, u32 code, u32 field,
+ const struct imx_media_pixfmt **cc)
+{
+ const struct imx_media_pixfmt *lcc;
+
+ mbus->width = width;
+ mbus->height = height;
+ mbus->field = field;
+ if (code == 0)
+ imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
+ lcc = imx_media_find_mbus_format(code, CS_SEL_ANY, false);
+ if (!lcc) {
+ lcc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ if (!lcc)
+ return -EINVAL;
+ }
+
+ mbus->code = code;
+ init_mbus_colorimetry(mbus, lcc);
+ if (cc)
+ *cc = lcc;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
+
+/*
+ * Check whether the field and colorimetry parameters in tryfmt are
+ * uninitialized, and if so fill them with the values from fmt,
+ * or if tryfmt->colorspace has been initialized, all the default
+ * colorimetry params can be derived from tryfmt->colorspace.
+ *
+ * tryfmt->code must be set on entry.
+ *
+ * If this format is destined to be routed through the Image Converter,
+ * quantization and Y`CbCr encoding must be fixed. The IC expects and
+ * produces fixed quantization and Y`CbCr encoding at its input and output
+ * (full range for RGB, limited range for YUV, and V4L2_YCBCR_ENC_601).
+ */
+void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
+ struct v4l2_mbus_framefmt *fmt,
+ bool ic_route)
+{
+ const struct imx_media_pixfmt *cc;
+ bool is_rgb = false;
+
+ cc = imx_media_find_mbus_format(tryfmt->code, CS_SEL_ANY, true);
+ if (!cc)
+ cc = imx_media_find_ipu_format(tryfmt->code, CS_SEL_ANY);
+ if (cc && cc->cs != IPUV3_COLORSPACE_YUV)
+ is_rgb = true;
+
+ /* fill field if necessary */
+ if (tryfmt->field == V4L2_FIELD_ANY)
+ tryfmt->field = fmt->field;
+
+ /* fill colorimetry if necessary */
+ if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) {
+ tryfmt->colorspace = fmt->colorspace;
+ tryfmt->xfer_func = fmt->xfer_func;
+ tryfmt->ycbcr_enc = fmt->ycbcr_enc;
+ tryfmt->quantization = fmt->quantization;
+ } else {
+ if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT) {
+ tryfmt->xfer_func =
+ V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
+ }
+ if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
+ tryfmt->ycbcr_enc =
+ V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
+ }
+ if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT) {
+ tryfmt->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(
+ is_rgb, tryfmt->colorspace,
+ tryfmt->ycbcr_enc);
+ }
+ }
+
+ if (ic_route) {
+ tryfmt->quantization = is_rgb ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ }
+}
+EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields);
+
+int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
+ struct v4l2_mbus_framefmt *mbus,
+ const struct imx_media_pixfmt *cc)
+{
+ u32 stride;
+
+ if (!cc) {
+ cc = imx_media_find_ipu_format(mbus->code, CS_SEL_ANY);
+ if (!cc)
+ cc = imx_media_find_mbus_format(mbus->code, CS_SEL_ANY,
+ true);
+ if (!cc)
+ return -EINVAL;
+ }
+
+ /*
+ * TODO: the IPU currently does not support the AYUV32 format,
+ * so until it does convert to a supported YUV format.
+ */
+ if (cc->ipufmt && cc->cs == IPUV3_COLORSPACE_YUV) {
+ u32 code;
+
+ imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
+ cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false);
+ }
+
+ stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3;
+
+ pix->width = mbus->width;
+ pix->height = mbus->height;
+ pix->pixelformat = cc->fourcc;
+ pix->colorspace = mbus->colorspace;
+ pix->xfer_func = mbus->xfer_func;
+ pix->ycbcr_enc = mbus->ycbcr_enc;
+ pix->quantization = mbus->quantization;
+ pix->field = mbus->field;
+ pix->bytesperline = stride;
+ pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt);
+
+int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
+ struct v4l2_mbus_framefmt *mbus)
+{
+ int ret;
+
+ memset(image, 0, sizeof(*image));
+
+ ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL);
+ if (ret)
+ return ret;
+
+ image->rect.width = mbus->width;
+ image->rect.height = mbus->height;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image);
+
+int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
+ struct ipu_image *image)
+{
+ const struct imx_media_pixfmt *fmt;
+
+ fmt = imx_media_find_format(image->pix.pixelformat, CS_SEL_ANY, true);
+ if (!fmt)
+ return -EINVAL;
+
+ memset(mbus, 0, sizeof(*mbus));
+ mbus->width = image->pix.width;
+ mbus->height = image->pix.height;
+ mbus->code = fmt->codes[0];
+ mbus->field = image->pix.field;
+ mbus->colorspace = image->pix.colorspace;
+ mbus->xfer_func = image->pix.xfer_func;
+ mbus->ycbcr_enc = image->pix.ycbcr_enc;
+ mbus->quantization = image->pix.quantization;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_ipu_image_to_mbus_fmt);
+
+void imx_media_free_dma_buf(struct imx_media_dev *imxmd,
+ struct imx_media_dma_buf *buf)
+{
+ if (buf->virt)
+ dma_free_coherent(imxmd->md.dev, buf->len,
+ buf->virt, buf->phys);
+
+ buf->virt = NULL;
+ buf->phys = 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_free_dma_buf);
+
+int imx_media_alloc_dma_buf(struct imx_media_dev *imxmd,
+ struct imx_media_dma_buf *buf,
+ int size)
+{
+ imx_media_free_dma_buf(imxmd, buf);
+
+ buf->len = PAGE_ALIGN(size);
+ buf->virt = dma_alloc_coherent(imxmd->md.dev, buf->len, &buf->phys,
+ GFP_DMA | GFP_KERNEL);
+ if (!buf->virt) {
+ dev_err(imxmd->md.dev, "failed to alloc dma buffer\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_alloc_dma_buf);
+
+/* form a subdev name given a group id and ipu id */
+void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id)
+{
+ int id;
+
+ switch (grp_id) {
+ case IMX_MEDIA_GRP_ID_CSI0...IMX_MEDIA_GRP_ID_CSI1:
+ id = (grp_id >> IMX_MEDIA_GRP_ID_CSI_BIT) - 1;
+ snprintf(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id);
+ break;
+ case IMX_MEDIA_GRP_ID_VDIC:
+ snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1);
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRP:
+ snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1);
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRPENC:
+ snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1);
+ break;
+ case IMX_MEDIA_GRP_ID_IC_PRPVF:
+ snprintf(sd_name, sz, "ipu%d_ic_prpvf", ipu_id + 1);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name);
+
+struct imx_media_subdev *
+imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd,
+ struct v4l2_subdev *sd)
+{
+ struct imx_media_subdev *imxsd;
+ int i;
+
+ for (i = 0; i < imxmd->num_subdevs; i++) {
+ imxsd = &imxmd->subdev[i];
+ if (sd == imxsd->sd)
+ return imxsd;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_sd);
+
+struct imx_media_subdev *
+imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, u32 grp_id)
+{
+ struct imx_media_subdev *imxsd;
+ int i;
+
+ for (i = 0; i < imxmd->num_subdevs; i++) {
+ imxsd = &imxmd->subdev[i];
+ if (imxsd->sd && imxsd->sd->grp_id == grp_id)
+ return imxsd;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id);
+
+/*
+ * Adds a video device to the master video device list. This is called by
+ * an async subdev that owns a video device when it is registered.
+ */
+int imx_media_add_video_device(struct imx_media_dev *imxmd,
+ struct imx_media_video_dev *vdev)
+{
+ int vdev_idx, ret = 0;
+
+ mutex_lock(&imxmd->mutex);
+
+ vdev_idx = imxmd->num_vdevs;
+ if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) {
+ dev_err(imxmd->md.dev,
+ "%s: too many video devices! can't add %s\n",
+ __func__, vdev->vfd->name);
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ imxmd->vdev[vdev_idx] = vdev;
+ imxmd->num_vdevs++;
+out:
+ mutex_unlock(&imxmd->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_add_video_device);
+
+/*
+ * Search upstream or downstream for a subdevice in the current pipeline
+ * with given grp_id, starting from start_entity. Returns the subdev's
+ * source/sink pad that it was reached from. Must be called with
+ * mdev->graph_mutex held.
+ */
+static struct media_pad *
+find_pipeline_pad(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity,
+ u32 grp_id, bool upstream)
+{
+ struct media_entity *me = start_entity;
+ struct media_pad *pad = NULL;
+ struct v4l2_subdev *sd;
+ int i;
+
+ for (i = 0; i < me->num_pads; i++) {
+ struct media_pad *spad = &me->pads[i];
+
+ if ((upstream && !(spad->flags & MEDIA_PAD_FL_SINK)) ||
+ (!upstream && !(spad->flags & MEDIA_PAD_FL_SOURCE)))
+ continue;
+
+ pad = media_entity_remote_pad(spad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ continue;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ if (sd->grp_id & grp_id)
+ return pad;
+
+ return find_pipeline_pad(imxmd, pad->entity, grp_id, upstream);
+ }
+
+ return NULL;
+}
+
+/*
+ * Search upstream for a subdev in the current pipeline with
+ * given grp_id. Must be called with mdev->graph_mutex held.
+ */
+static struct v4l2_subdev *
+find_upstream_subdev(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity,
+ u32 grp_id)
+{
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+
+ if (is_media_entity_v4l2_subdev(start_entity)) {
+ sd = media_entity_to_v4l2_subdev(start_entity);
+ if (sd->grp_id & grp_id)
+ return sd;
+ }
+
+ pad = find_pipeline_pad(imxmd, start_entity, grp_id, true);
+
+ return pad ? media_entity_to_v4l2_subdev(pad->entity) : NULL;
+}
+
+
+/*
+ * Find the upstream mipi-csi2 virtual channel reached from the given
+ * start entity in the current pipeline.
+ * Must be called with mdev->graph_mutex held.
+ */
+int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity)
+{
+ struct media_pad *pad;
+ int ret = -EPIPE;
+
+ pad = find_pipeline_pad(imxmd, start_entity, IMX_MEDIA_GRP_ID_CSI2,
+ true);
+ if (pad) {
+ ret = pad->index - 1;
+ dev_dbg(imxmd->md.dev, "found vc%d from %s\n",
+ ret, start_entity->name);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_find_mipi_csi2_channel);
+
+/*
+ * Find a subdev reached upstream from the given start entity in
+ * the current pipeline.
+ * Must be called with mdev->graph_mutex held.
+ */
+struct imx_media_subdev *
+imx_media_find_upstream_subdev(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity,
+ u32 grp_id)
+{
+ struct v4l2_subdev *sd;
+
+ sd = find_upstream_subdev(imxmd, start_entity, grp_id);
+ if (!sd)
+ return ERR_PTR(-ENODEV);
+
+ return imx_media_find_subdev_by_sd(imxmd, sd);
+}
+EXPORT_SYMBOL_GPL(imx_media_find_upstream_subdev);
+
+struct imx_media_subdev *
+__imx_media_find_sensor(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity)
+{
+ return imx_media_find_upstream_subdev(imxmd, start_entity,
+ IMX_MEDIA_GRP_ID_SENSOR);
+}
+EXPORT_SYMBOL_GPL(__imx_media_find_sensor);
+
+struct imx_media_subdev *
+imx_media_find_sensor(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity)
+{
+ struct imx_media_subdev *sensor;
+
+ mutex_lock(&imxmd->md.graph_mutex);
+ sensor = __imx_media_find_sensor(imxmd, start_entity);
+ mutex_unlock(&imxmd->md.graph_mutex);
+
+ return sensor;
+}
+EXPORT_SYMBOL_GPL(imx_media_find_sensor);
+
+/*
+ * Turn current pipeline streaming on/off starting from entity.
+ */
+int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
+ struct media_entity *entity,
+ bool on)
+{
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ if (!is_media_entity_v4l2_subdev(entity))
+ return -EINVAL;
+ sd = media_entity_to_v4l2_subdev(entity);
+
+ mutex_lock(&imxmd->md.graph_mutex);
+
+ if (on) {
+ ret = __media_pipeline_start(entity, &imxmd->pipe);
+ if (ret)
+ goto out;
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (ret)
+ __media_pipeline_stop(entity);
+ } else {
+ v4l2_subdev_call(sd, video, s_stream, 0);
+ if (entity->pipe)
+ __media_pipeline_stop(entity);
+ }
+
+out:
+ mutex_unlock(&imxmd->md.graph_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_pipeline_set_stream);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
new file mode 100644
index 000000000000..7eabdc4aa79f
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -0,0 +1,1009 @@
+/*
+ * V4L2 Deinterlacer Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+/*
+ * This subdev implements two different video pipelines:
+ *
+ * CSI -> VDIC
+ *
+ * In this pipeline, the CSI sends a single interlaced field F(n-1)
+ * directly to the VDIC (and optionally the following field F(n)
+ * can be sent to memory via IDMAC channel 13). This pipeline only works
+ * in VDIC's high motion mode, which only requires a single field for
+ * processing. The other motion modes (low and medium) require three
+ * fields, so this pipeline does not work in those modes. Also, it is
+ * not clear how this pipeline can deal with the various field orders
+ * (sequential BT/TB, interlaced BT/TB).
+ *
+ * MEM -> CH8,9,10 -> VDIC
+ *
+ * In this pipeline, previous field F(n-1), current field F(n), and next
+ * field F(n+1) are transferred to the VDIC via IDMAC channels 8,9,10.
+ * These memory buffers can come from a video output or mem2mem device.
+ * All motion modes are supported by this pipeline.
+ *
+ * The "direct" CSI->VDIC pipeline requires no DMA, but it can only be
+ * used in high motion mode.
+ */
+
+struct vdic_priv;
+
+struct vdic_pipeline_ops {
+ int (*setup)(struct vdic_priv *priv);
+ void (*start)(struct vdic_priv *priv);
+ void (*stop)(struct vdic_priv *priv);
+ void (*disable)(struct vdic_priv *priv);
+};
+
+/*
+ * Min/Max supported width and heights.
+ */
+#define MIN_W 176
+#define MIN_H 144
+#define MAX_W_VDIC 968
+#define MAX_H_VDIC 2048
+#define W_ALIGN 4 /* multiple of 16 pixels */
+#define H_ALIGN 1 /* multiple of 2 lines */
+#define S_ALIGN 1 /* multiple of 2 */
+
+struct vdic_priv {
+ struct device *dev;
+ struct ipu_soc *ipu;
+ struct imx_media_dev *md;
+ struct v4l2_subdev sd;
+ struct media_pad pad[VDIC_NUM_PADS];
+ int ipu_id;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ /* IPU units we require */
+ struct ipu_vdi *vdi;
+
+ int active_input_pad;
+
+ struct ipuv3_channel *vdi_in_ch_p; /* F(n-1) transfer channel */
+ struct ipuv3_channel *vdi_in_ch; /* F(n) transfer channel */
+ struct ipuv3_channel *vdi_in_ch_n; /* F(n+1) transfer channel */
+
+ /* pipeline operations */
+ struct vdic_pipeline_ops *ops;
+
+ /* current and previous input buffers indirect path */
+ struct imx_media_buffer *curr_in_buf;
+ struct imx_media_buffer *prev_in_buf;
+
+ /*
+ * translated field type, input line stride, and field size
+ * for indirect path
+ */
+ u32 fieldtype;
+ u32 in_stride;
+ u32 field_size;
+
+ /* the source (a video device or subdev) */
+ struct media_entity *src;
+ /* the sink that will receive the progressive out buffers */
+ struct v4l2_subdev *sink_sd;
+
+ struct v4l2_mbus_framefmt format_mbus[VDIC_NUM_PADS];
+ const struct imx_media_pixfmt *cc[VDIC_NUM_PADS];
+ struct v4l2_fract frame_interval[VDIC_NUM_PADS];
+
+ /* the video device at IDMAC input pad */
+ struct imx_media_video_dev *vdev;
+
+ bool csi_direct; /* using direct CSI->VDIC->IC pipeline */
+
+ /* motion select control */
+ struct v4l2_ctrl_handler ctrl_hdlr;
+ enum ipu_motion_sel motion;
+
+ int stream_count;
+};
+
+static void vdic_put_ipu_resources(struct vdic_priv *priv)
+{
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch_p))
+ ipu_idmac_put(priv->vdi_in_ch_p);
+ priv->vdi_in_ch_p = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch))
+ ipu_idmac_put(priv->vdi_in_ch);
+ priv->vdi_in_ch = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch_n))
+ ipu_idmac_put(priv->vdi_in_ch_n);
+ priv->vdi_in_ch_n = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi))
+ ipu_vdi_put(priv->vdi);
+ priv->vdi = NULL;
+}
+
+static int vdic_get_ipu_resources(struct vdic_priv *priv)
+{
+ int ret, err_chan;
+
+ priv->ipu = priv->md->ipu[priv->ipu_id];
+
+ priv->vdi = ipu_vdi_get(priv->ipu);
+ if (IS_ERR(priv->vdi)) {
+ v4l2_err(&priv->sd, "failed to get VDIC\n");
+ ret = PTR_ERR(priv->vdi);
+ goto out;
+ }
+
+ if (!priv->csi_direct) {
+ priv->vdi_in_ch_p = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_PREV);
+ if (IS_ERR(priv->vdi_in_ch_p)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_PREV;
+ ret = PTR_ERR(priv->vdi_in_ch_p);
+ goto out_err_chan;
+ }
+
+ priv->vdi_in_ch = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_CUR);
+ if (IS_ERR(priv->vdi_in_ch)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_CUR;
+ ret = PTR_ERR(priv->vdi_in_ch);
+ goto out_err_chan;
+ }
+
+ priv->vdi_in_ch_n = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_NEXT);
+ if (IS_ERR(priv->vdi_in_ch_n)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_NEXT;
+ ret = PTR_ERR(priv->vdi_in_ch_n);
+ goto out_err_chan;
+ }
+ }
+
+ return 0;
+
+out_err_chan:
+ v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan);
+out:
+ vdic_put_ipu_resources(priv);
+ return ret;
+}
+
+/*
+ * This function is currently unused, but will be called when the
+ * output/mem2mem device at the IDMAC input pad sends us a new
+ * buffer. It kicks off the IDMAC read channels to bring in the
+ * buffer fields from memory and begin the conversions.
+ */
+static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv,
+ struct imx_media_buffer *curr)
+{
+ dma_addr_t prev_phys, curr_phys, next_phys;
+ struct imx_media_buffer *prev;
+ struct vb2_buffer *curr_vb, *prev_vb;
+ u32 fs = priv->field_size;
+ u32 is = priv->in_stride;
+
+ /* current input buffer is now previous */
+ priv->prev_in_buf = priv->curr_in_buf;
+ priv->curr_in_buf = curr;
+ prev = priv->prev_in_buf ? priv->prev_in_buf : curr;
+
+ prev_vb = &prev->vbuf.vb2_buf;
+ curr_vb = &curr->vbuf.vb2_buf;
+
+ switch (priv->fieldtype) {
+ case V4L2_FIELD_SEQ_TB:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs;
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is;
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
+ break;
+ default:
+ /* assume V4L2_FIELD_INTERLACED_TB */
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ break;
+ }
+
+ ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys);
+ ipu_cpmem_set_buffer(priv->vdi_in_ch, 0, curr_phys);
+ ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0, next_phys);
+
+ ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0);
+ ipu_idmac_select_buffer(priv->vdi_in_ch, 0);
+ ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0);
+}
+
+static int setup_vdi_channel(struct vdic_priv *priv,
+ struct ipuv3_channel *channel,
+ dma_addr_t phys0, dma_addr_t phys1)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ unsigned int burst_size;
+ struct ipu_image image;
+ int ret;
+
+ ipu_cpmem_zero(channel);
+
+ memset(&image, 0, sizeof(image));
+ image.pix = vdev->fmt.fmt.pix;
+ /* one field to VDIC channels */
+ image.pix.height /= 2;
+ image.rect.width = image.pix.width;
+ image.rect.height = image.pix.height;
+ image.phys0 = phys0;
+ image.phys1 = phys1;
+
+ ret = ipu_cpmem_set_image(channel, &image);
+ if (ret)
+ return ret;
+
+ burst_size = (image.pix.width & 0xf) ? 8 : 16;
+ ipu_cpmem_set_burstsize(channel, burst_size);
+
+ ipu_cpmem_set_axi_id(channel, 1);
+
+ ipu_idmac_set_double_buffer(channel, false);
+
+ return 0;
+}
+
+static int vdic_setup_direct(struct vdic_priv *priv)
+{
+ /* set VDIC to receive from CSI for direct path */
+ ipu_fsu_link(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
+ IPUV3_CHANNEL_CSI_VDI_PREV);
+
+ return 0;
+}
+
+static void vdic_start_direct(struct vdic_priv *priv)
+{
+}
+
+static void vdic_stop_direct(struct vdic_priv *priv)
+{
+}
+
+static void vdic_disable_direct(struct vdic_priv *priv)
+{
+ ipu_fsu_unlink(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
+ IPUV3_CHANNEL_CSI_VDI_PREV);
+}
+
+static int vdic_setup_indirect(struct vdic_priv *priv)
+{
+ struct v4l2_mbus_framefmt *infmt;
+ const struct imx_media_pixfmt *incc;
+ int in_size, ret;
+
+ infmt = &priv->format_mbus[VDIC_SINK_PAD_IDMAC];
+ incc = priv->cc[VDIC_SINK_PAD_IDMAC];
+
+ in_size = (infmt->width * incc->bpp * infmt->height) >> 3;
+
+ /* 1/2 full image size */
+ priv->field_size = in_size / 2;
+ priv->in_stride = incc->planar ?
+ infmt->width : (infmt->width * incc->bpp) >> 3;
+
+ priv->prev_in_buf = NULL;
+ priv->curr_in_buf = NULL;
+
+ priv->fieldtype = infmt->field;
+
+ /* init the vdi-in channels */
+ ret = setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0);
+ if (ret)
+ return ret;
+ ret = setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0);
+ if (ret)
+ return ret;
+ return setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0);
+}
+
+static void vdic_start_indirect(struct vdic_priv *priv)
+{
+ /* enable the channels */
+ ipu_idmac_enable_channel(priv->vdi_in_ch_p);
+ ipu_idmac_enable_channel(priv->vdi_in_ch);
+ ipu_idmac_enable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_stop_indirect(struct vdic_priv *priv)
+{
+ /* disable channels */
+ ipu_idmac_disable_channel(priv->vdi_in_ch_p);
+ ipu_idmac_disable_channel(priv->vdi_in_ch);
+ ipu_idmac_disable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_disable_indirect(struct vdic_priv *priv)
+{
+}
+
+static struct vdic_pipeline_ops direct_ops = {
+ .setup = vdic_setup_direct,
+ .start = vdic_start_direct,
+ .stop = vdic_stop_direct,
+ .disable = vdic_disable_direct,
+};
+
+static struct vdic_pipeline_ops indirect_ops = {
+ .setup = vdic_setup_indirect,
+ .start = vdic_start_indirect,
+ .stop = vdic_stop_indirect,
+ .disable = vdic_disable_indirect,
+};
+
+static int vdic_start(struct vdic_priv *priv)
+{
+ struct v4l2_mbus_framefmt *infmt;
+ int ret;
+
+ infmt = &priv->format_mbus[priv->active_input_pad];
+
+ priv->ops = priv->csi_direct ? &direct_ops : &indirect_ops;
+
+ ret = vdic_get_ipu_resources(priv);
+ if (ret)
+ return ret;
+
+ /*
+ * init the VDIC.
+ *
+ * note we don't give infmt->code to ipu_vdi_setup(). The VDIC
+ * only supports 4:2:2 or 4:2:0, and this subdev will only
+ * negotiate 4:2:2 at its sink pads.
+ */
+ ipu_vdi_setup(priv->vdi, MEDIA_BUS_FMT_UYVY8_2X8,
+ infmt->width, infmt->height);
+ ipu_vdi_set_field_order(priv->vdi, V4L2_STD_UNKNOWN, infmt->field);
+ ipu_vdi_set_motion(priv->vdi, priv->motion);
+
+ ret = priv->ops->setup(priv);
+ if (ret)
+ goto out_put_ipu;
+
+ ipu_vdi_enable(priv->vdi);
+
+ priv->ops->start(priv);
+
+ return 0;
+
+out_put_ipu:
+ vdic_put_ipu_resources(priv);
+ return ret;
+}
+
+static void vdic_stop(struct vdic_priv *priv)
+{
+ priv->ops->stop(priv);
+ ipu_vdi_disable(priv->vdi);
+ priv->ops->disable(priv);
+
+ vdic_put_ipu_resources(priv);
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int vdic_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vdic_priv *priv = container_of(ctrl->handler,
+ struct vdic_priv, ctrl_hdlr);
+ enum ipu_motion_sel motion;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_DEINTERLACING_MODE:
+ motion = ctrl->val;
+ if (motion != priv->motion) {
+ /* can't change motion control mid-streaming */
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->motion = motion;
+ }
+ break;
+ default:
+ v4l2_err(&priv->sd, "Invalid control\n");
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vdic_ctrl_ops = {
+ .s_ctrl = vdic_s_ctrl,
+};
+
+static const char * const vdic_ctrl_motion_menu[] = {
+ "No Motion Compensation",
+ "Low Motion",
+ "Medium Motion",
+ "High Motion",
+};
+
+static int vdic_init_controls(struct vdic_priv *priv)
+{
+ struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdlr, 1);
+
+ v4l2_ctrl_new_std_menu_items(hdlr, &vdic_ctrl_ops,
+ V4L2_CID_DEINTERLACING_MODE,
+ HIGH_MOTION, 0, HIGH_MOTION,
+ vdic_ctrl_motion_menu);
+
+ priv->sd.ctrl_handler = hdlr;
+
+ if (hdlr->error) {
+ ret = hdlr->error;
+ goto out_free;
+ }
+
+ v4l2_ctrl_handler_setup(hdlr);
+ return 0;
+
+out_free:
+ v4l2_ctrl_handler_free(hdlr);
+ return ret;
+}
+
+static int vdic_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd = NULL;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->src || !priv->sink_sd) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (priv->csi_direct)
+ src_sd = media_entity_to_v4l2_subdev(priv->src);
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (priv->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(priv->dev, "stream %s\n", enable ? "ON" : "OFF");
+
+ if (enable)
+ ret = vdic_start(priv);
+ else
+ vdic_stop(priv);
+ if (ret)
+ goto out;
+
+ if (src_sd) {
+ /* start/stop upstream */
+ ret = v4l2_subdev_call(src_sd, video, s_stream, enable);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret) {
+ if (enable)
+ vdic_stop(priv);
+ goto out;
+ }
+ }
+
+update_count:
+ priv->stream_count += enable ? 1 : -1;
+ if (priv->stream_count < 0)
+ priv->stream_count = 0;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
+ else
+ return &priv->format_mbus[pad];
+}
+
+static int vdic_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_YUV);
+}
+
+static int vdic_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdformat->format = *fmt;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static void vdic_try_fmt(struct vdic_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat,
+ const struct imx_media_pixfmt **cc)
+{
+ struct v4l2_mbus_framefmt *infmt;
+
+ *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_YUV);
+ if (!*cc) {
+ u32 code;
+
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ *cc = imx_media_find_ipu_format(code, CS_SEL_YUV);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad,
+ sdformat->which);
+
+ switch (sdformat->pad) {
+ case VDIC_SRC_PAD_DIRECT:
+ sdformat->format = *infmt;
+ /* output is always progressive! */
+ sdformat->format.field = V4L2_FIELD_NONE;
+ break;
+ case VDIC_SINK_PAD_DIRECT:
+ case VDIC_SINK_PAD_IDMAC:
+ v4l_bound_align_image(&sdformat->format.width,
+ MIN_W, MAX_W_VDIC, W_ALIGN,
+ &sdformat->format.height,
+ MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN);
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
+
+ /* input must be interlaced! Choose SEQ_TB if not */
+ if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field))
+ sdformat->format.field = V4L2_FIELD_SEQ_TB;
+ break;
+ }
+}
+
+static int vdic_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ vdic_try_fmt(priv, cfg, sdformat, &cc);
+
+ fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ *fmt = sdformat->format;
+
+ /* propagate format to source pad */
+ if (sdformat->pad == VDIC_SINK_PAD_DIRECT ||
+ sdformat->pad == VDIC_SINK_PAD_IDMAC) {
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *outfmt;
+ struct v4l2_subdev_format format;
+
+ format.pad = VDIC_SRC_PAD_DIRECT;
+ format.which = sdformat->which;
+ format.format = sdformat->format;
+ vdic_try_fmt(priv, cfg, &format, &outcc);
+
+ outfmt = __vdic_get_fmt(priv, cfg, VDIC_SRC_PAD_DIRECT,
+ sdformat->which);
+ *outfmt = format.format;
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[VDIC_SRC_PAD_DIRECT] = outcc;
+ }
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[sdformat->pad] = cc;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ mutex_lock(&priv->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SOURCE) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->sink_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->sink_sd = remote_sd;
+ } else {
+ priv->sink_sd = NULL;
+ }
+
+ goto out;
+ }
+
+ /* this is a sink pad */
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->src) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ priv->src = NULL;
+ goto out;
+ }
+
+ if (local->index == VDIC_SINK_PAD_IDMAC) {
+ struct imx_media_video_dev *vdev = priv->vdev;
+
+ if (!is_media_entity_v4l2_video_device(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!vdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ priv->csi_direct = false;
+ } else {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ /* direct pad must connect to a CSI */
+ if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_CSI) ||
+ remote->index != CSI_SRC_PAD_DIRECT) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->csi_direct = true;
+ }
+
+ priv->src = remote->entity;
+ /* record which input pad is now active */
+ priv->active_input_pad = local->index;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = v4l2_subdev_link_validate_default(sd, link,
+ source_fmt, sink_fmt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->csi_direct && priv->motion != HIGH_MOTION) {
+ v4l2_err(&priv->sd,
+ "direct CSI pipeline requires high motion\n");
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ if (fi->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fi->interval = priv->frame_interval[fi->pad];
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int vdic_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi, *output_fi;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ input_fi = &priv->frame_interval[priv->active_input_pad];
+ output_fi = &priv->frame_interval[VDIC_SRC_PAD_DIRECT];
+
+ switch (fi->pad) {
+ case VDIC_SINK_PAD_DIRECT:
+ case VDIC_SINK_PAD_IDMAC:
+ /* No limits on input frame interval */
+ /* Reset output interval */
+ *output_fi = fi->interval;
+ if (priv->csi_direct)
+ output_fi->denominator *= 2;
+ break;
+ case VDIC_SRC_PAD_DIRECT:
+ /*
+ * frame rate at output pad is double input
+ * rate when using direct CSI->VDIC pipeline.
+ *
+ * TODO: implement VDIC frame skipping
+ */
+ fi->interval = *input_fi;
+ if (priv->csi_direct)
+ fi->interval.denominator *= 2;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->frame_interval[fi->pad] = fi->interval;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int vdic_registered(struct v4l2_subdev *sd)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ int i, ret;
+ u32 code;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ for (i = 0; i < VDIC_NUM_PADS; i++) {
+ priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ?
+ MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+
+ code = 0;
+ if (i != VDIC_SINK_PAD_IDMAC)
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
+ 640, 480, code, V4L2_FIELD_NONE,
+ &priv->cc[i]);
+ if (ret)
+ return ret;
+
+ /* init default frame interval */
+ priv->frame_interval[i].numerator = 1;
+ priv->frame_interval[i].denominator = 30;
+ if (i == VDIC_SRC_PAD_DIRECT)
+ priv->frame_interval[i].denominator *= 2;
+ }
+
+ priv->active_input_pad = VDIC_SINK_PAD_DIRECT;
+
+ ret = vdic_init_controls(priv);
+ if (ret)
+ return ret;
+
+ ret = media_entity_pads_init(&sd->entity, VDIC_NUM_PADS, priv->pad);
+ if (ret)
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+
+ return ret;
+}
+
+static void vdic_unregistered(struct v4l2_subdev *sd)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+}
+
+static const struct v4l2_subdev_pad_ops vdic_pad_ops = {
+ .enum_mbus_code = vdic_enum_mbus_code,
+ .get_fmt = vdic_get_fmt,
+ .set_fmt = vdic_set_fmt,
+ .link_validate = vdic_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops vdic_video_ops = {
+ .g_frame_interval = vdic_g_frame_interval,
+ .s_frame_interval = vdic_s_frame_interval,
+ .s_stream = vdic_s_stream,
+};
+
+static const struct media_entity_operations vdic_entity_ops = {
+ .link_setup = vdic_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_ops vdic_subdev_ops = {
+ .video = &vdic_video_ops,
+ .pad = &vdic_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops vdic_internal_ops = {
+ .registered = vdic_registered,
+ .unregistered = vdic_unregistered,
+};
+
+static int imx_vdic_probe(struct platform_device *pdev)
+{
+ struct imx_media_internal_sd_platformdata *pdata;
+ struct vdic_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, &priv->sd);
+ priv->dev = &pdev->dev;
+
+ pdata = priv->dev->platform_data;
+ priv->ipu_id = pdata->ipu_id;
+
+ v4l2_subdev_init(&priv->sd, &vdic_subdev_ops);
+ v4l2_set_subdevdata(&priv->sd, priv);
+ priv->sd.internal_ops = &vdic_internal_ops;
+ priv->sd.entity.ops = &vdic_entity_ops;
+ priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ priv->sd.dev = &pdev->dev;
+ priv->sd.owner = THIS_MODULE;
+ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ /* get our group id */
+ priv->sd.grp_id = pdata->grp_id;
+ strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
+
+ mutex_init(&priv->lock);
+
+ ret = v4l2_async_register_subdev(&priv->sd);
+ if (ret)
+ goto free;
+
+ return 0;
+free:
+ mutex_destroy(&priv->lock);
+ return ret;
+}
+
+static int imx_vdic_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ v4l2_info(sd, "Removing\n");
+
+ v4l2_async_unregister_subdev(sd);
+ mutex_destroy(&priv->lock);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct platform_device_id imx_vdic_ids[] = {
+ { .name = "imx-ipuv3-vdic" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, imx_vdic_ids);
+
+static struct platform_driver imx_vdic_driver = {
+ .probe = imx_vdic_probe,
+ .remove = imx_vdic_remove,
+ .id_table = imx_vdic_ids,
+ .driver = {
+ .name = "imx-ipuv3-vdic",
+ },
+};
+module_platform_driver(imx_vdic_driver);
+
+MODULE_DESCRIPTION("i.MX VDIC subdev driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-vdic");
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
new file mode 100644
index 000000000000..d409170632bd
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media.h
@@ -0,0 +1,325 @@
+/*
+ * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_MEDIA_H
+#define _IMX_MEDIA_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+
+/*
+ * This is somewhat arbitrary, but we need at least:
+ * - 4 video devices per IPU
+ * - 3 IC subdevs per IPU
+ * - 1 VDIC subdev per IPU
+ * - 2 CSI subdevs per IPU
+ * - 1 mipi-csi2 receiver subdev
+ * - 2 video-mux subdevs
+ * - 2 camera sensor subdevs per IPU (1 parallel, 1 mipi-csi2)
+ *
+ */
+/* max video devices */
+#define IMX_MEDIA_MAX_VDEVS 8
+/* max subdevices */
+#define IMX_MEDIA_MAX_SUBDEVS 32
+/* max pads per subdev */
+#define IMX_MEDIA_MAX_PADS 16
+/* max links per pad */
+#define IMX_MEDIA_MAX_LINKS 8
+
+/*
+ * Pad definitions for the subdevs with multiple source or
+ * sink pads
+ */
+
+/* ipu_csi */
+enum {
+ CSI_SINK_PAD = 0,
+ CSI_SRC_PAD_DIRECT,
+ CSI_SRC_PAD_IDMAC,
+ CSI_NUM_PADS,
+};
+
+#define CSI_NUM_SINK_PADS 1
+#define CSI_NUM_SRC_PADS 2
+
+/* ipu_vdic */
+enum {
+ VDIC_SINK_PAD_DIRECT = 0,
+ VDIC_SINK_PAD_IDMAC,
+ VDIC_SRC_PAD_DIRECT,
+ VDIC_NUM_PADS,
+};
+
+#define VDIC_NUM_SINK_PADS 2
+#define VDIC_NUM_SRC_PADS 1
+
+/* ipu_ic_prp */
+enum {
+ PRP_SINK_PAD = 0,
+ PRP_SRC_PAD_PRPENC,
+ PRP_SRC_PAD_PRPVF,
+ PRP_NUM_PADS,
+};
+
+#define PRP_NUM_SINK_PADS 1
+#define PRP_NUM_SRC_PADS 2
+
+/* ipu_ic_prpencvf */
+enum {
+ PRPENCVF_SINK_PAD = 0,
+ PRPENCVF_SRC_PAD,
+ PRPENCVF_NUM_PADS,
+};
+
+#define PRPENCVF_NUM_SINK_PADS 1
+#define PRPENCVF_NUM_SRC_PADS 1
+
+/* How long to wait for EOF interrupts in the buffer-capture subdevs */
+#define IMX_MEDIA_EOF_TIMEOUT 1000
+
+struct imx_media_pixfmt {
+ u32 fourcc;
+ u32 codes[4];
+ int bpp; /* total bpp */
+ enum ipu_color_space cs;
+ bool planar; /* is a planar format */
+ bool bayer; /* is a raw bayer format */
+ bool ipufmt; /* is one of the IPU internal formats */
+};
+
+struct imx_media_buffer {
+ struct vb2_v4l2_buffer vbuf; /* v4l buffer must be first */
+ struct list_head list;
+};
+
+struct imx_media_video_dev {
+ struct video_device *vfd;
+
+ /* the user format */
+ struct v4l2_format fmt;
+ const struct imx_media_pixfmt *cc;
+};
+
+static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ return container_of(vbuf, struct imx_media_buffer, vbuf);
+}
+
+struct imx_media_link {
+ struct device_node *remote_sd_node;
+ char remote_devname[32];
+ int local_pad;
+ int remote_pad;
+};
+
+struct imx_media_pad {
+ struct media_pad pad;
+ struct imx_media_link link[IMX_MEDIA_MAX_LINKS];
+ bool devnode; /* does this pad link to a device node */
+ int num_links;
+
+ /*
+ * list of video devices that can be reached from this pad,
+ * list is only valid for source pads.
+ */
+ struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS];
+ int num_vdevs;
+};
+
+struct imx_media_internal_sd_platformdata {
+ char sd_name[V4L2_SUBDEV_NAME_SIZE];
+ u32 grp_id;
+ int ipu_id;
+};
+
+struct imx_media_subdev {
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *sd; /* set when bound */
+
+ struct imx_media_pad pad[IMX_MEDIA_MAX_PADS];
+ int num_sink_pads;
+ int num_src_pads;
+
+ /* the platform device if this is an internal subdev */
+ struct platform_device *pdev;
+ /* the devname is needed for async devname match */
+ char devname[32];
+
+ /* if this is a sensor */
+ struct v4l2_fwnode_endpoint sensor_ep;
+};
+
+struct imx_media_dev {
+ struct media_device md;
+ struct v4l2_device v4l2_dev;
+
+ /* the pipeline object */
+ struct media_pipeline pipe;
+
+ struct mutex mutex; /* protect elements below */
+
+ /* master subdevice list */
+ struct imx_media_subdev subdev[IMX_MEDIA_MAX_SUBDEVS];
+ int num_subdevs;
+
+ /* master video device list */
+ struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS];
+ int num_vdevs;
+
+ /* IPUs this media driver control, valid after subdevs bound */
+ struct ipu_soc *ipu[2];
+
+ /* for async subdev registration */
+ struct v4l2_async_subdev *async_ptrs[IMX_MEDIA_MAX_SUBDEVS];
+ struct v4l2_async_notifier subdev_notifier;
+};
+
+enum codespace_sel {
+ CS_SEL_YUV = 0,
+ CS_SEL_RGB,
+ CS_SEL_ANY,
+};
+
+const struct imx_media_pixfmt *
+imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer);
+int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel);
+const struct imx_media_pixfmt *
+imx_media_find_mbus_format(u32 code, enum codespace_sel cs_sel,
+ bool allow_bayer);
+int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel,
+ bool allow_bayer);
+const struct imx_media_pixfmt *
+imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel);
+int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel);
+
+int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
+ u32 width, u32 height, u32 code, u32 field,
+ const struct imx_media_pixfmt **cc);
+void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
+ struct v4l2_mbus_framefmt *fmt,
+ bool ic_route);
+int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
+ struct v4l2_mbus_framefmt *mbus,
+ const struct imx_media_pixfmt *cc);
+int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
+ struct v4l2_mbus_framefmt *mbus);
+int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
+ struct ipu_image *image);
+
+struct imx_media_subdev *
+imx_media_find_async_subdev(struct imx_media_dev *imxmd,
+ struct device_node *np,
+ const char *devname);
+struct imx_media_subdev *
+imx_media_add_async_subdev(struct imx_media_dev *imxmd,
+ struct device_node *np,
+ struct platform_device *pdev);
+int imx_media_add_pad_link(struct imx_media_dev *imxmd,
+ struct imx_media_pad *pad,
+ struct device_node *remote_node,
+ const char *remote_devname,
+ int local_pad, int remote_pad);
+
+void imx_media_grp_id_to_sd_name(char *sd_name, int sz,
+ u32 grp_id, int ipu_id);
+
+int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *csi[4]);
+void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd);
+
+struct imx_media_subdev *
+imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd,
+ struct v4l2_subdev *sd);
+struct imx_media_subdev *
+imx_media_find_subdev_by_id(struct imx_media_dev *imxmd,
+ u32 grp_id);
+int imx_media_add_video_device(struct imx_media_dev *imxmd,
+ struct imx_media_video_dev *vdev);
+int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity);
+struct imx_media_subdev *
+imx_media_find_upstream_subdev(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity,
+ u32 grp_id);
+struct imx_media_subdev *
+__imx_media_find_sensor(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity);
+struct imx_media_subdev *
+imx_media_find_sensor(struct imx_media_dev *imxmd,
+ struct media_entity *start_entity);
+
+struct imx_media_dma_buf {
+ void *virt;
+ dma_addr_t phys;
+ unsigned long len;
+};
+
+void imx_media_free_dma_buf(struct imx_media_dev *imxmd,
+ struct imx_media_dma_buf *buf);
+int imx_media_alloc_dma_buf(struct imx_media_dev *imxmd,
+ struct imx_media_dma_buf *buf,
+ int size);
+
+int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
+ struct media_entity *entity,
+ bool on);
+
+/* imx-media-fim.c */
+struct imx_media_fim;
+void imx_media_fim_eof_monitor(struct imx_media_fim *fim, struct timespec *ts);
+int imx_media_fim_set_stream(struct imx_media_fim *fim,
+ const struct v4l2_fract *frame_interval,
+ bool on);
+int imx_media_fim_add_controls(struct imx_media_fim *fim);
+struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd);
+void imx_media_fim_free(struct imx_media_fim *fim);
+
+/* imx-media-of.c */
+struct imx_media_subdev *
+imx_media_of_find_subdev(struct imx_media_dev *imxmd,
+ struct device_node *np,
+ const char *name);
+int imx_media_of_parse(struct imx_media_dev *dev,
+ struct imx_media_subdev *(*csi)[4],
+ struct device_node *np);
+
+/* imx-media-capture.c */
+struct imx_media_video_dev *
+imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad);
+void imx_media_capture_device_remove(struct imx_media_video_dev *vdev);
+int imx_media_capture_device_register(struct imx_media_video_dev *vdev);
+void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev);
+struct imx_media_buffer *
+imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev);
+void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
+ struct v4l2_pix_format *pix);
+void imx_media_capture_device_error(struct imx_media_video_dev *vdev);
+
+/* subdev group ids */
+#define IMX_MEDIA_GRP_ID_SENSOR (1 << 8)
+#define IMX_MEDIA_GRP_ID_VIDMUX (1 << 9)
+#define IMX_MEDIA_GRP_ID_CSI2 (1 << 10)
+#define IMX_MEDIA_GRP_ID_CSI_BIT 11
+#define IMX_MEDIA_GRP_ID_CSI (0x3 << IMX_MEDIA_GRP_ID_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_CSI0 (1 << IMX_MEDIA_GRP_ID_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_CSI1 (2 << IMX_MEDIA_GRP_ID_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_VDIC (1 << 13)
+#define IMX_MEDIA_GRP_ID_IC_PRP (1 << 14)
+#define IMX_MEDIA_GRP_ID_IC_PRPENC (1 << 15)
+#define IMX_MEDIA_GRP_ID_IC_PRPVF (1 << 16)
+
+#endif
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
new file mode 100644
index 000000000000..5061f3f524fd
--- /dev/null
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -0,0 +1,698 @@
+/*
+ * MIPI CSI-2 Receiver Subdev for Freescale i.MX6 SOC.
+ *
+ * Copyright (c) 2012-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include "imx-media.h"
+
+/*
+ * there must be 5 pads: 1 input pad from sensor, and
+ * the 4 virtual channel output pads
+ */
+#define CSI2_SINK_PAD 0
+#define CSI2_NUM_SINK_PADS 1
+#define CSI2_NUM_SRC_PADS 4
+#define CSI2_NUM_PADS 5
+
+/*
+ * The default maximum bit-rate per lane in Mbps, if the
+ * source subdev does not provide V4L2_CID_LINK_FREQ.
+ */
+#define CSI2_DEFAULT_MAX_MBPS 849
+
+struct csi2_dev {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad[CSI2_NUM_PADS];
+ struct clk *dphy_clk;
+ struct clk *pllref_clk;
+ struct clk *pix_clk; /* what is this? */
+ void __iomem *base;
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ struct v4l2_mbus_framefmt format_mbus;
+
+ int stream_count;
+ struct v4l2_subdev *src_sd;
+ bool sink_linked[CSI2_NUM_SRC_PADS];
+};
+
+#define DEVICE_NAME "imx6-mipi-csi2"
+
+/* Register offsets */
+#define CSI2_VERSION 0x000
+#define CSI2_N_LANES 0x004
+#define CSI2_PHY_SHUTDOWNZ 0x008
+#define CSI2_DPHY_RSTZ 0x00c
+#define CSI2_RESETN 0x010
+#define CSI2_PHY_STATE 0x014
+#define PHY_STOPSTATEDATA_BIT 4
+#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n))
+#define PHY_RXCLKACTIVEHS BIT(8)
+#define PHY_RXULPSCLKNOT BIT(9)
+#define PHY_STOPSTATECLK BIT(10)
+#define CSI2_DATA_IDS_1 0x018
+#define CSI2_DATA_IDS_2 0x01c
+#define CSI2_ERR1 0x020
+#define CSI2_ERR2 0x024
+#define CSI2_MSK1 0x028
+#define CSI2_MSK2 0x02c
+#define CSI2_PHY_TST_CTRL0 0x030
+#define PHY_TESTCLR BIT(0)
+#define PHY_TESTCLK BIT(1)
+#define CSI2_PHY_TST_CTRL1 0x034
+#define PHY_TESTEN BIT(16)
+/*
+ * i.MX CSI2IPU Gasket registers follow. The CSI2IPU gasket is
+ * not part of the MIPI CSI-2 core, but its registers fall in the
+ * same register map range.
+ */
+#define CSI2IPU_GASKET 0xf00
+#define CSI2IPU_YUV422_YUYV BIT(2)
+
+static inline struct csi2_dev *sd_to_dev(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi2_dev, sd);
+}
+
+/*
+ * The required sequence of MIPI CSI-2 startup as specified in the i.MX6
+ * reference manual is as follows:
+ *
+ * 1. Deassert presetn signal (global reset).
+ * It's not clear what this "global reset" signal is (maybe APB
+ * global reset), but in any case this step would be probably
+ * be carried out during driver load in csi2_probe().
+ *
+ * 2. Configure MIPI Camera Sensor to put all Tx lanes in LP-11 state.
+ * This must be carried out by the MIPI sensor's s_power(ON) subdev
+ * op.
+ *
+ * 3. D-PHY initialization.
+ * 4. CSI2 Controller programming (Set N_LANES, deassert PHY_SHUTDOWNZ,
+ * deassert PHY_RSTZ, deassert CSI2_RESETN).
+ * 5. Read the PHY status register (PHY_STATE) to confirm that all data and
+ * clock lanes of the D-PHY are in LP-11 state.
+ * 6. Configure the MIPI Camera Sensor to start transmitting a clock on the
+ * D-PHY clock lane.
+ * 7. CSI2 Controller programming - Read the PHY status register (PHY_STATE)
+ * to confirm that the D-PHY is receiving a clock on the D-PHY clock lane.
+ *
+ * All steps 3 through 7 are carried out by csi2_s_stream(ON) here. Step
+ * 6 is accomplished by calling the source subdev's s_stream(ON) between
+ * steps 5 and 7.
+ */
+
+static void csi2_enable(struct csi2_dev *csi2, bool enable)
+{
+ if (enable) {
+ writel(0x1, csi2->base + CSI2_PHY_SHUTDOWNZ);
+ writel(0x1, csi2->base + CSI2_DPHY_RSTZ);
+ writel(0x1, csi2->base + CSI2_RESETN);
+ } else {
+ writel(0x0, csi2->base + CSI2_PHY_SHUTDOWNZ);
+ writel(0x0, csi2->base + CSI2_DPHY_RSTZ);
+ writel(0x0, csi2->base + CSI2_RESETN);
+ }
+}
+
+static void csi2_set_lanes(struct csi2_dev *csi2)
+{
+ int lanes = csi2->bus.num_data_lanes;
+
+ writel(lanes - 1, csi2->base + CSI2_N_LANES);
+}
+
+static void dw_mipi_csi2_phy_write(struct csi2_dev *csi2,
+ u32 test_code, u32 test_data)
+{
+ /* Clear PHY test interface */
+ writel(PHY_TESTCLR, csi2->base + CSI2_PHY_TST_CTRL0);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Raise test interface strobe signal */
+ writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Configure address write on falling edge and lower strobe signal */
+ writel(PHY_TESTEN | test_code, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Configure data write on rising edge and raise strobe signal */
+ writel(test_data, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Clear strobe signal */
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+}
+
+/*
+ * This table is based on the table documented at
+ * https://community.nxp.com/docs/DOC-94312. It assumes
+ * a 27MHz D-PHY pll reference clock.
+ */
+static const struct {
+ u32 max_mbps;
+ u32 hsfreqrange_sel;
+} hsfreq_map[] = {
+ { 90, 0x00}, {100, 0x20}, {110, 0x40}, {125, 0x02},
+ {140, 0x22}, {150, 0x42}, {160, 0x04}, {180, 0x24},
+ {200, 0x44}, {210, 0x06}, {240, 0x26}, {250, 0x46},
+ {270, 0x08}, {300, 0x28}, {330, 0x48}, {360, 0x2a},
+ {400, 0x4a}, {450, 0x0c}, {500, 0x2c}, {550, 0x0e},
+ {600, 0x2e}, {650, 0x10}, {700, 0x30}, {750, 0x12},
+ {800, 0x32}, {850, 0x14}, {900, 0x34}, {950, 0x54},
+ {1000, 0x74},
+};
+
+static int max_mbps_to_hsfreqrange_sel(u32 max_mbps)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hsfreq_map); i++)
+ if (hsfreq_map[i].max_mbps > max_mbps)
+ return hsfreq_map[i].hsfreqrange_sel;
+
+ return -EINVAL;
+}
+
+static int csi2_dphy_init(struct csi2_dev *csi2)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 mbps_per_lane;
+ int sel;
+
+ ctrl = v4l2_ctrl_find(csi2->src_sd->ctrl_handler,
+ V4L2_CID_LINK_FREQ);
+ if (!ctrl)
+ mbps_per_lane = CSI2_DEFAULT_MAX_MBPS;
+ else
+ mbps_per_lane = DIV_ROUND_UP_ULL(2 * ctrl->qmenu_int[ctrl->val],
+ USEC_PER_SEC);
+
+ sel = max_mbps_to_hsfreqrange_sel(mbps_per_lane);
+ if (sel < 0)
+ return sel;
+
+ dw_mipi_csi2_phy_write(csi2, 0x44, sel);
+
+ return 0;
+}
+
+/*
+ * Waits for ultra-low-power state on D-PHY clock lane. This is currently
+ * unused and may not be needed at all, but keep around just in case.
+ */
+static int __maybe_unused csi2_dphy_wait_ulp(struct csi2_dev *csi2)
+{
+ u32 reg;
+ int ret;
+
+ /* wait for ULP on clock lane */
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ !(reg & PHY_RXULPSCLKNOT), 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "ULP timeout, phy_state = 0x%08x\n", reg);
+ return ret;
+ }
+
+ /* wait until no errors on bus */
+ ret = readl_poll_timeout(csi2->base + CSI2_ERR1, reg,
+ reg == 0x0, 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "stable bus timeout, err1 = 0x%08x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Waits for low-power LP-11 state on data and clock lanes. */
+static int csi2_dphy_wait_stopstate(struct csi2_dev *csi2)
+{
+ u32 mask, reg;
+ int ret;
+
+ mask = PHY_STOPSTATECLK |
+ ((csi2->bus.num_data_lanes - 1) << PHY_STOPSTATEDATA_BIT);
+
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ (reg & mask) == mask, 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "LP-11 timeout, phy_state = 0x%08x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Wait for active clock on the clock lane. */
+static int csi2_dphy_wait_clock_lane(struct csi2_dev *csi2)
+{
+ u32 reg;
+ int ret;
+
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ (reg & PHY_RXCLKACTIVEHS), 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "clock lane timeout, phy_state = 0x%08x\n",
+ reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Setup the i.MX CSI2IPU Gasket */
+static void csi2ipu_gasket_init(struct csi2_dev *csi2)
+{
+ u32 reg = 0;
+
+ switch (csi2->format_mbus.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ reg = CSI2IPU_YUV422_YUYV;
+ break;
+ default:
+ break;
+ }
+
+ writel(reg, csi2->base + CSI2IPU_GASKET);
+}
+
+static int csi2_start(struct csi2_dev *csi2)
+{
+ int ret;
+
+ ret = clk_prepare_enable(csi2->pix_clk);
+ if (ret)
+ return ret;
+
+ /* setup the gasket */
+ csi2ipu_gasket_init(csi2);
+
+ /* Step 3 */
+ ret = csi2_dphy_init(csi2);
+ if (ret)
+ goto err_disable_clk;
+
+ /* Step 4 */
+ csi2_set_lanes(csi2);
+ csi2_enable(csi2, true);
+
+ /* Step 5 */
+ ret = csi2_dphy_wait_stopstate(csi2);
+ if (ret)
+ goto err_assert_reset;
+
+ /* Step 6 */
+ ret = v4l2_subdev_call(csi2->src_sd, video, s_stream, 1);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret)
+ goto err_assert_reset;
+
+ /* Step 7 */
+ ret = csi2_dphy_wait_clock_lane(csi2);
+ if (ret)
+ goto err_stop_upstream;
+
+ return 0;
+
+err_stop_upstream:
+ v4l2_subdev_call(csi2->src_sd, video, s_stream, 0);
+err_assert_reset:
+ csi2_enable(csi2, false);
+err_disable_clk:
+ clk_disable_unprepare(csi2->pix_clk);
+ return ret;
+}
+
+static void csi2_stop(struct csi2_dev *csi2)
+{
+ /* stop upstream */
+ v4l2_subdev_call(csi2->src_sd, video, s_stream, 0);
+
+ csi2_enable(csi2, false);
+ clk_disable_unprepare(csi2->pix_clk);
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int csi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int i, ret = 0;
+
+ mutex_lock(&csi2->lock);
+
+ if (!csi2->src_sd) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ for (i = 0; i < CSI2_NUM_SRC_PADS; i++) {
+ if (csi2->sink_linked[i])
+ break;
+ }
+ if (i >= CSI2_NUM_SRC_PADS) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (csi2->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(csi2->dev, "stream %s\n", enable ? "ON" : "OFF");
+ if (enable)
+ ret = csi2_start(csi2);
+ else
+ csi2_stop(csi2);
+ if (ret)
+ goto out;
+
+update_count:
+ csi2->stream_count += enable ? 1 : -1;
+ if (csi2->stream_count < 0)
+ csi2->stream_count = 0;
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+static int csi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(csi2->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ mutex_lock(&csi2->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SOURCE) {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->sink_linked[local->index - 1]) {
+ ret = -EBUSY;
+ goto out;
+ }
+ csi2->sink_linked[local->index - 1] = true;
+ } else {
+ csi2->sink_linked[local->index - 1] = false;
+ }
+ } else {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ csi2->src_sd = remote_sd;
+ } else {
+ csi2->src_sd = NULL;
+ }
+ }
+
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+static int csi2_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ mutex_lock(&csi2->lock);
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt = v4l2_subdev_get_try_format(&csi2->sd, cfg,
+ sdformat->pad);
+ else
+ fmt = &csi2->format_mbus;
+
+ sdformat->format = *fmt;
+
+ mutex_unlock(&csi2->lock);
+
+ return 0;
+}
+
+static int csi2_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int ret = 0;
+
+ if (sdformat->pad >= CSI2_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&csi2->lock);
+
+ if (csi2->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Output pads mirror active input pad, no limits on input pads */
+ if (sdformat->pad != CSI2_SINK_PAD)
+ sdformat->format = csi2->format_mbus;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ cfg->try_fmt = sdformat->format;
+ else
+ csi2->format_mbus = sdformat->format;
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int csi2_registered(struct v4l2_subdev *sd)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int i, ret;
+
+ for (i = 0; i < CSI2_NUM_PADS; i++) {
+ csi2->pad[i].flags = (i == CSI2_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+ }
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&csi2->format_mbus,
+ 640, 480, 0, V4L2_FIELD_NONE, NULL);
+ if (ret)
+ return ret;
+
+ return media_entity_pads_init(&sd->entity, CSI2_NUM_PADS, csi2->pad);
+}
+
+static const struct media_entity_operations csi2_entity_ops = {
+ .link_setup = csi2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+ .s_stream = csi2_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .get_fmt = csi2_get_fmt,
+ .set_fmt = csi2_set_fmt,
+};
+
+static const struct v4l2_subdev_ops csi2_subdev_ops = {
+ .video = &csi2_video_ops,
+ .pad = &csi2_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .registered = csi2_registered,
+};
+
+static int csi2_parse_endpoints(struct csi2_dev *csi2)
+{
+ struct device_node *node = csi2->dev->of_node;
+ struct device_node *epnode;
+ struct v4l2_fwnode_endpoint ep;
+
+ epnode = of_graph_get_endpoint_by_regs(node, 0, -1);
+ if (!epnode) {
+ v4l2_err(&csi2->sd, "failed to get sink endpoint node\n");
+ return -EINVAL;
+ }
+
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(epnode), &ep);
+ of_node_put(epnode);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n");
+ return -EINVAL;
+ }
+
+ csi2->bus = ep.bus.mipi_csi2;
+
+ dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes);
+ dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags);
+ return 0;
+}
+
+static int csi2_probe(struct platform_device *pdev)
+{
+ struct csi2_dev *csi2;
+ struct resource *res;
+ int ret;
+
+ csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+ if (!csi2)
+ return -ENOMEM;
+
+ csi2->dev = &pdev->dev;
+
+ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
+ v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
+ csi2->sd.internal_ops = &csi2_internal_ops;
+ csi2->sd.entity.ops = &csi2_entity_ops;
+ csi2->sd.dev = &pdev->dev;
+ csi2->sd.owner = THIS_MODULE;
+ csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strcpy(csi2->sd.name, DEVICE_NAME);
+ csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi2->sd.grp_id = IMX_MEDIA_GRP_ID_CSI2;
+
+ ret = csi2_parse_endpoints(csi2);
+ if (ret)
+ return ret;
+
+ csi2->pllref_clk = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(csi2->pllref_clk)) {
+ v4l2_err(&csi2->sd, "failed to get pll reference clock\n");
+ ret = PTR_ERR(csi2->pllref_clk);
+ return ret;
+ }
+
+ csi2->dphy_clk = devm_clk_get(&pdev->dev, "dphy");
+ if (IS_ERR(csi2->dphy_clk)) {
+ v4l2_err(&csi2->sd, "failed to get dphy clock\n");
+ ret = PTR_ERR(csi2->dphy_clk);
+ return ret;
+ }
+
+ csi2->pix_clk = devm_clk_get(&pdev->dev, "pix");
+ if (IS_ERR(csi2->pix_clk)) {
+ v4l2_err(&csi2->sd, "failed to get pixel clock\n");
+ ret = PTR_ERR(csi2->pix_clk);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ v4l2_err(&csi2->sd, "failed to get platform resources\n");
+ return -ENODEV;
+ }
+
+ csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+ if (!csi2->base) {
+ v4l2_err(&csi2->sd, "failed to map CSI-2 registers\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&csi2->lock);
+
+ ret = clk_prepare_enable(csi2->pllref_clk);
+ if (ret) {
+ v4l2_err(&csi2->sd, "failed to enable pllref_clk\n");
+ goto rmmutex;
+ }
+
+ ret = clk_prepare_enable(csi2->dphy_clk);
+ if (ret) {
+ v4l2_err(&csi2->sd, "failed to enable dphy_clk\n");
+ goto pllref_off;
+ }
+
+ platform_set_drvdata(pdev, &csi2->sd);
+
+ ret = v4l2_async_register_subdev(&csi2->sd);
+ if (ret)
+ goto dphy_off;
+
+ return 0;
+
+dphy_off:
+ clk_disable_unprepare(csi2->dphy_clk);
+pllref_off:
+ clk_disable_unprepare(csi2->pllref_clk);
+rmmutex:
+ mutex_destroy(&csi2->lock);
+ return ret;
+}
+
+static int csi2_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ clk_disable_unprepare(csi2->dphy_clk);
+ clk_disable_unprepare(csi2->pllref_clk);
+ mutex_destroy(&csi2->lock);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct of_device_id csi2_dt_ids[] = {
+ { .compatible = "fsl,imx6-mipi-csi2", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, csi2_dt_ids);
+
+static struct platform_driver csi2_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = csi2_dt_ids,
+ },
+ .probe = csi2_probe,
+ .remove = csi2_remove,
+};
+
+module_platform_driver(csi2_driver);
+
+MODULE_DESCRIPTION("i.MX5/6 MIPI CSI-2 Receiver driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/lirc/TODO b/drivers/staging/media/lirc/TODO
index cbea5d84fed3..a97800a8e127 100644
--- a/drivers/staging/media/lirc/TODO
+++ b/drivers/staging/media/lirc/TODO
@@ -1,13 +1,36 @@
-- All drivers should either be ported to ir-core, or dropped entirely
- (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
- example of a previously completed port).
-
-- lirc_bt829 uses registers on a Mach64 VT, which has a separate kernel
- framebuffer driver (atyfb) and userland X driver (mach64). It can't
- simply be converted to a normal PCI driver, but ideally it should be
- coordinated with the other drivers.
-
-Please send patches to:
-Jarod Wilson <jarod@wilsonet.com>
-Greg Kroah-Hartman <greg@kroah.com>
+1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for
+the chips supported by lirc_zilog. Before moving lirc_zilog out of staging:
+
+a. ir-kbd-i2c needs a module parameter added to allow the user to tell
+ ir-kbd-i2c to ignore Z8 IR units.
+
+b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c
+ does.
+
+
+2. lirc_zilog module ref-counting need examination. It has not been
+verified that cdev and lirc_dev will take the proper module references on
+lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node
+is open.
+
+(The good news is ref-counting of lirc_zilog internal structures appears to be
+complete. Testing has shown the cx18 module can be unloaded out from under
+irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse
+effects. The cx18 module could then be reloaded and irw properly began
+receiving button presses again and ir_send worked without error.)
+
+
+3. Bridge drivers, if able, should provide a chip reset() callback
+to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines
+to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog
+to bring the chip back to normal when it hangs, in the same places the
+original lirc_pvr150 driver code does. This is not strictly needed, so it
+is not required to move lirc_zilog out of staging.
+
+Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed
+and installed on Hauppauge products. When working on either module, developers
+must consider at least the following bridge drivers which mention an IR Rx unit
+at address 0x71 (indicative of a Z8):
+
+ ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134
diff --git a/drivers/staging/media/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog
deleted file mode 100644
index a97800a8e127..000000000000
--- a/drivers/staging/media/lirc/TODO.lirc_zilog
+++ /dev/null
@@ -1,36 +0,0 @@
-1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for
-the chips supported by lirc_zilog. Before moving lirc_zilog out of staging:
-
-a. ir-kbd-i2c needs a module parameter added to allow the user to tell
- ir-kbd-i2c to ignore Z8 IR units.
-
-b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c
- does.
-
-
-2. lirc_zilog module ref-counting need examination. It has not been
-verified that cdev and lirc_dev will take the proper module references on
-lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node
-is open.
-
-(The good news is ref-counting of lirc_zilog internal structures appears to be
-complete. Testing has shown the cx18 module can be unloaded out from under
-irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse
-effects. The cx18 module could then be reloaded and irw properly began
-receiving button presses again and ir_send worked without error.)
-
-
-3. Bridge drivers, if able, should provide a chip reset() callback
-to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines
-to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog
-to bring the chip back to normal when it hangs, in the same places the
-original lirc_pvr150 driver code does. This is not strictly needed, so it
-is not required to move lirc_zilog out of staging.
-
-Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed
-and installed on Hauppauge products. When working on either module, developers
-must consider at least the following bridge drivers which mention an IR Rx unit
-at address 0x71 (indicative of a Z8):
-
- ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134
-
diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
index 8ce1db04414a..015e41bd036e 100644
--- a/drivers/staging/media/lirc/lirc_zilog.c
+++ b/drivers/staging/media/lirc/lirc_zilog.c
@@ -156,7 +156,6 @@ static struct mutex tx_data_lock;
/* module parameters */
static bool debug; /* debug output */
static bool tx_only; /* only handle the IR Tx function */
-static int minor = -1; /* minor number */
/* struct IR reference counting */
@@ -184,10 +183,11 @@ static void release_ir_device(struct kref *ref)
* ir->open_count == 0 - happens on final close()
* ir_lock, tx_ref_lock, rx_ref_lock, all released
*/
- if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
+ if (ir->l.minor >= 0) {
lirc_unregister_driver(ir->l.minor);
- ir->l.minor = MAX_IRCTL_DEVICES;
+ ir->l.minor = -1;
}
+
if (kfifo_initialized(&ir->rbuf.fifo))
lirc_buffer_free(&ir->rbuf);
list_del(&ir->list);
@@ -215,7 +215,7 @@ static struct IR_rx *get_ir_rx(struct IR *ir)
spin_lock(&ir->rx_ref_lock);
rx = ir->rx;
- if (rx != NULL)
+ if (rx)
kref_get(&rx->ref);
spin_unlock(&ir->rx_ref_lock);
return rx;
@@ -277,7 +277,7 @@ static struct IR_tx *get_ir_tx(struct IR *ir)
spin_lock(&ir->tx_ref_lock);
tx = ir->tx;
- if (tx != NULL)
+ if (tx)
kref_get(&tx->ref);
spin_unlock(&ir->tx_ref_lock);
return tx;
@@ -327,12 +327,12 @@ static int add_to_buf(struct IR *ir)
}
rx = get_ir_rx(ir);
- if (rx == NULL)
+ if (!rx)
return -ENXIO;
/* Ensure our rx->c i2c_client remains valid for the duration */
mutex_lock(&rx->client_lock);
- if (rx->c == NULL) {
+ if (!rx->c) {
mutex_unlock(&rx->client_lock);
put_ir_rx(rx, false);
return -ENXIO;
@@ -388,7 +388,7 @@ static int add_to_buf(struct IR *ir)
break;
}
schedule_timeout((100 * HZ + 999) / 1000);
- if (tx != NULL)
+ if (tx)
tx->need_boot = 1;
++failures;
@@ -444,7 +444,7 @@ static int add_to_buf(struct IR *ir)
} while (!lirc_buffer_full(rbuf));
mutex_unlock(&rx->client_lock);
- if (tx != NULL)
+ if (tx)
put_ir_tx(tx, false);
put_ir_rx(rx, false);
return ret;
@@ -472,7 +472,7 @@ static int lirc_thread(void *arg)
/* if device not opened, we can sleep half a second */
if (atomic_read(&ir->open_count) == 0) {
- schedule_timeout(HZ/2);
+ schedule_timeout(HZ / 2);
continue;
}
@@ -497,18 +497,9 @@ static int lirc_thread(void *arg)
return 0;
}
-static int set_use_inc(void *data)
-{
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
-}
-
/* safe read of a uint32 (always network byte order) */
static int read_uint32(unsigned char **data,
- unsigned char *endp, unsigned int *val)
+ unsigned char *endp, unsigned int *val)
{
if (*data + 4 > endp)
return 0;
@@ -520,7 +511,7 @@ static int read_uint32(unsigned char **data,
/* safe read of a uint8 */
static int read_uint8(unsigned char **data,
- unsigned char *endp, unsigned char *val)
+ unsigned char *endp, unsigned char *val)
{
if (*data + 1 > endp)
return 0;
@@ -530,7 +521,7 @@ static int read_uint8(unsigned char **data,
/* safe skipping of N bytes */
static int skip(unsigned char **data,
- unsigned char *endp, unsigned int distance)
+ unsigned char *endp, unsigned int distance)
{
if (*data + distance > endp)
return 0;
@@ -540,7 +531,7 @@ static int skip(unsigned char **data,
/* decompress key data into the given buffer */
static int get_key_data(unsigned char *buf,
- unsigned int codeset, unsigned int key)
+ unsigned int codeset, unsigned int key)
{
unsigned char *data, *endp, *diffs, *key_block;
unsigned char keys, ndiffs, id;
@@ -554,9 +545,9 @@ static int get_key_data(unsigned char *buf,
if (!read_uint32(&data, tx_data->endp, &i))
goto corrupt;
- if (i == codeset)
+ if (i == codeset) {
break;
- else if (codeset > i) {
+ } else if (codeset > i) {
base = pos + 1;
--lim;
}
@@ -772,7 +763,7 @@ static int fw_load(struct IR_tx *tx)
/* Parse the file */
tx_data = vmalloc(sizeof(*tx_data));
- if (tx_data == NULL) {
+ if (!tx_data) {
release_firmware(fw_entry);
ret = -ENOMEM;
goto out;
@@ -781,7 +772,7 @@ static int fw_load(struct IR_tx *tx)
/* Copy the data so hotplug doesn't get confused and timeout */
tx_data->datap = vmalloc(fw_entry->size);
- if (tx_data->datap == NULL) {
+ if (!tx_data->datap) {
release_firmware(fw_entry);
vfree(tx_data);
ret = -ENOMEM;
@@ -810,7 +801,7 @@ static int fw_load(struct IR_tx *tx)
goto corrupt;
if (!read_uint32(&data, tx_data->endp,
- &tx_data->num_code_sets))
+ &tx_data->num_code_sets))
goto corrupt;
dev_dbg(tx->ir->l.dev, "%u IR blaster codesets loaded\n",
@@ -818,7 +809,7 @@ static int fw_load(struct IR_tx *tx)
tx_data->code_sets = vmalloc(
tx_data->num_code_sets * sizeof(char *));
- if (tx_data->code_sets == NULL) {
+ if (!tx_data->code_sets) {
fw_unload_locked();
ret = -ENOMEM;
goto out;
@@ -866,12 +857,12 @@ static int fw_load(struct IR_tx *tx)
* global fixed
*/
if (!skip(&data, tx_data->endp,
- 1 + TX_BLOCK_SIZE - num_global_fixed))
+ 1 + TX_BLOCK_SIZE - num_global_fixed))
goto corrupt;
/* Then we have keys-1 blocks of key id+diffs */
if (!skip(&data, tx_data->endp,
- (ndiffs + 1) * (keys - 1)))
+ (ndiffs + 1) * (keys - 1)))
goto corrupt;
}
ret = 0;
@@ -905,7 +896,7 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n,
}
rx = get_ir_rx(ir);
- if (rx == NULL)
+ if (!rx)
return -ENXIO;
/*
@@ -990,8 +981,9 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
"failed to get data for code %u, key %u -- check lircd.conf entries\n",
code, key);
return ret;
- } else if (ret != 0)
+ } else if (ret != 0) {
return ret;
+ }
/* Send the data block */
ret = send_data_block(tx, data_block);
@@ -1065,7 +1057,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
break;
dev_dbg(tx->ir->l.dev,
"NAK expected: i2c_master_send failed with %d (try %d)\n",
- ret, i+1);
+ ret, i + 1);
}
if (ret != 1) {
dev_err(tx->ir->l.dev,
@@ -1111,12 +1103,12 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n,
/* Get a struct IR_tx reference */
tx = get_ir_tx(ir);
- if (tx == NULL)
+ if (!tx)
return -ENXIO;
/* Ensure our tx->c i2c_client remains valid for the duration */
mutex_lock(&tx->client_lock);
- if (tx->c == NULL) {
+ if (!tx->c) {
mutex_unlock(&tx->client_lock);
put_ir_tx(tx, false);
return -ENXIO;
@@ -1188,8 +1180,9 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n,
schedule_timeout((100 * HZ + 999) / 1000);
tx->need_boot = 1;
++failures;
- } else
+ } else {
i += sizeof(int);
+ }
}
/* Release i2c bus */
@@ -1212,15 +1205,15 @@ static unsigned int poll(struct file *filep, poll_table *wait)
struct lirc_buffer *rbuf = ir->l.rbuf;
unsigned int ret;
- dev_dbg(ir->l.dev, "poll called\n");
+ dev_dbg(ir->l.dev, "%s called\n", __func__);
rx = get_ir_rx(ir);
- if (rx == NULL) {
+ if (!rx) {
/*
* Revisit this, if our poll function ever reports writeable
* status for Tx
*/
- dev_dbg(ir->l.dev, "poll result = POLLERR\n");
+ dev_dbg(ir->l.dev, "%s result = POLLERR\n", __func__);
return POLLERR;
}
@@ -1231,9 +1224,9 @@ static unsigned int poll(struct file *filep, poll_table *wait)
poll_wait(filep, &rbuf->wait_poll, wait);
/* Indicate what ops could happen immediately without blocking */
- ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM);
+ ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN | POLLRDNORM);
- dev_dbg(ir->l.dev, "poll result = %s\n",
+ dev_dbg(ir->l.dev, "%s result = %s\n", __func__,
ret ? "POLLIN|POLLRDNORM" : "none");
return ret;
}
@@ -1255,15 +1248,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
result = put_user(features, uptr);
break;
case LIRC_GET_REC_MODE:
- if (!(features&LIRC_CAN_REC_MASK))
+ if (!(features & LIRC_CAN_REC_MASK))
return -ENOSYS;
result = put_user(LIRC_REC2MODE
- (features&LIRC_CAN_REC_MASK),
+ (features & LIRC_CAN_REC_MASK),
uptr);
break;
case LIRC_SET_REC_MODE:
- if (!(features&LIRC_CAN_REC_MASK))
+ if (!(features & LIRC_CAN_REC_MASK))
return -ENOSYS;
result = get_user(mode, uptr);
@@ -1271,13 +1264,13 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
result = -EINVAL;
break;
case LIRC_GET_SEND_MODE:
- if (!(features&LIRC_CAN_SEND_MASK))
+ if (!(features & LIRC_CAN_SEND_MASK))
return -ENOSYS;
result = put_user(LIRC_MODE_PULSE, uptr);
break;
case LIRC_SET_SEND_MODE:
- if (!(features&LIRC_CAN_SEND_MASK))
+ if (!(features & LIRC_CAN_SEND_MASK))
return -ENOSYS;
result = get_user(mode, uptr);
@@ -1322,7 +1315,7 @@ static int open(struct inode *node, struct file *filep)
/* find our IR struct */
ir = get_ir_device_by_minor(minor);
- if (ir == NULL)
+ if (!ir)
return -ENODEV;
atomic_inc(&ir->open_count);
@@ -1340,8 +1333,9 @@ static int close(struct inode *node, struct file *filep)
/* find our IR struct */
struct IR *ir = filep->private_data;
- if (ir == NULL) {
- pr_err("ir: close: no private_data attached to the file!\n");
+ if (!ir) {
+ pr_err("ir: %s: no private_data attached to the file!\n",
+ __func__);
return -ENODEV;
}
@@ -1394,10 +1388,7 @@ static struct lirc_driver lirc_template = {
.minor = -1,
.code_length = 13,
.buffer_size = BUFLEN / 2,
- .sample_rate = 0, /* tell lirc_dev to not start its own kthread */
.chunk_size = 2,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
.fops = &lirc_fops,
.owner = THIS_MODULE,
};
@@ -1407,7 +1398,7 @@ static int ir_remove(struct i2c_client *client)
if (strncmp("ir_tx_z8", client->name, 8) == 0) {
struct IR_tx *tx = i2c_get_clientdata(client);
- if (tx != NULL) {
+ if (tx) {
mutex_lock(&tx->client_lock);
tx->c = NULL;
mutex_unlock(&tx->client_lock);
@@ -1416,7 +1407,7 @@ static int ir_remove(struct i2c_client *client)
} else if (strncmp("ir_rx_z8", client->name, 8) == 0) {
struct IR_rx *rx = i2c_get_clientdata(client);
- if (rx != NULL) {
+ if (rx) {
mutex_lock(&rx->client_lock);
rx->c = NULL;
mutex_unlock(&rx->client_lock);
@@ -1426,7 +1417,6 @@ static int ir_remove(struct i2c_client *client)
return 0;
}
-
/* ir_devices_lock must be held */
static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter)
{
@@ -1467,14 +1457,14 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENXIO;
pr_info("probing IR %s on %s (i2c-%d)\n",
- tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+ tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
mutex_lock(&ir_devices_lock);
/* Use a single struct IR instance for both the Rx and Tx functions */
ir = get_ir_device_by_adapter(adap);
- if (ir == NULL) {
- ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+ if (!ir) {
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
if (!ir) {
ret = -ENOMEM;
goto out_no_ir;
@@ -1514,7 +1504,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
rx = get_ir_rx(ir);
/* Set up a struct IR_tx instance */
- tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
+ tx = kzalloc(sizeof(*tx), GFP_KERNEL);
if (!tx) {
ret = -ENOMEM;
goto out_put_xx;
@@ -1547,7 +1537,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
fw_load(tx);
/* Proceed only if the Rx client is also ready or not needed */
- if (rx == NULL && !tx_only) {
+ if (!rx && !tx_only) {
dev_info(tx->ir->l.dev,
"probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n",
adap->name, adap->nr);
@@ -1558,7 +1548,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
tx = get_ir_tx(ir);
/* Set up a struct IR_rx instance */
- rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL);
+ rx = kzalloc(sizeof(*rx), GFP_KERNEL);
if (!rx) {
ret = -ENOMEM;
goto out_put_xx;
@@ -1601,20 +1591,19 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
/* Proceed only if the Tx client is also ready */
- if (tx == NULL) {
+ if (!tx) {
pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n",
- adap->name, adap->nr);
+ adap->name, adap->nr);
goto out_ok;
}
}
/* register with lirc */
- ir->l.minor = minor; /* module option: user requested minor number */
ir->l.minor = lirc_register_driver(&ir->l);
- if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
+ if (ir->l.minor < 0) {
dev_err(tx->ir->l.dev,
- "%s: \"minor\" must be between 0 and %d (%d)!\n",
- __func__, MAX_IRCTL_DEVICES-1, ir->l.minor);
+ "%s: lirc_register_driver() failed: %i\n",
+ __func__, ir->l.minor);
ret = -EBADRQC;
goto out_put_xx;
}
@@ -1623,9 +1612,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
adap->name, adap->nr, ir->l.minor);
out_ok:
- if (rx != NULL)
+ if (rx)
put_ir_rx(rx, true);
- if (tx != NULL)
+ if (tx)
put_ir_tx(tx, true);
put_ir_device(ir, true);
dev_info(ir->l.dev,
@@ -1635,10 +1624,10 @@ out_ok:
return 0;
out_put_xx:
- if (rx != NULL)
+ if (rx)
put_ir_rx(rx, true);
out_put_tx:
- if (tx != NULL)
+ if (tx)
put_ir_tx(tx, true);
out_put_ir:
put_ir_device(ir, true);
@@ -1686,9 +1675,6 @@ MODULE_LICENSE("GPL");
/* for compat with old name, which isn't all that accurate anymore */
MODULE_ALIAS("lirc_pvr150");
-module_param(minor, int, 0444);
-MODULE_PARM_DESC(minor, "Preferred minor device number");
-
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Enable debugging messages");