diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-09 14:45:54 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-09 14:45:54 -0800 |
commit | 96a6de1a541c86e9e67b9c310c14db4099bd1cbc (patch) | |
tree | e77eb0e998e996f53dd0709611bd8cdd8776ee70 /drivers/staging | |
parent | 36011ddc78395b59a8a418c37f20bcc18828f1ef (diff) | |
parent | 15d90a6ae98e6d2c68497b44a491cb9efbb98ab1 (diff) | |
download | linux-96a6de1a541c86e9e67b9c310c14db4099bd1cbc.tar.gz linux-96a6de1a541c86e9e67b9c310c14db4099bd1cbc.tar.bz2 linux-96a6de1a541c86e9e67b9c310c14db4099bd1cbc.zip |
Merge tag 'media/v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- remove sensor drivers that got converted from soc_camera
- remaining soc_camera drivers got moved to staging
- some documentation cleanups and improvements
- the imx staging driver now supports imx7
- the ov9640, mt9m001 and mt9m111 got converted from soc_camera
- the vim2m driver now does what a m2m convert driver expects to do
- epoll() fixes on media subsystems
- several drivers fixes, typos, cleanups and improvements
* tag 'media/v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (346 commits)
media: dvb/earth-pt1: fix wrong initialization for demod blocks
media: vim2m: Address some coding style issues
media: vim2m: don't use BUG()
media: vim2m: speedup passthrough copy
media: vim2m: add an horizontal scaler
media: vim2m: don't accept YUYV anymore as output format
media: vim2m: add vertical linear scaler
media: vim2m: better handle cap/out buffers with different sizes
media: vim2m: use different framesizes for bayer formats
media: vim2m: add support for VIDIOC_ENUM_FRAMESIZES
media: vim2m: ensure that width is multiple of two
media: vim2m: improve debug messages
media: vim2m: add bayer capture formats
media: a few more typos at staging, pci, platform, radio and usb
media: Documentation: fix several typos
media: staging: fix several typos
media: include: fix several typos
media: common: fix several typos
media: v4l2-core: fix several typos
media: usb: fix several typos
...
Diffstat (limited to 'drivers/staging')
71 files changed, 9892 insertions, 1160 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 19cadd17e542..1da5c20d65c0 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,10 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" -source "drivers/staging/media/imx074/Kconfig" - -source "drivers/staging/media/mt9t031/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rockchip/vpu/Kconfig" @@ -41,4 +37,6 @@ source "drivers/staging/media/zoran/Kconfig" source "drivers/staging/media/ipu3/Kconfig" +source "drivers/staging/media/soc_camera/Kconfig" + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index edde1960b030..0355e3030504 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,8 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/ -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ @@ -10,3 +8,4 @@ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_SOC_CAMERA) += soc_camera/ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c index 5618c804c7e4..565a3dc5bed1 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -781,7 +781,7 @@ ipipe_set_3d_lut_regs(void __iomem *base_addr, void __iomem *isp5_base_addr, if (!lut_3d->en) return; - /* valied table */ + /* valid table */ tbl = lut_3d->table; for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { /* diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 625d0aa8367f..0a6d038fcec9 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -675,7 +675,7 @@ static void isif_config_bclamp(struct vpfe_isif_device *isif, val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) << ISIF_BC_MODE_COLOR_SHIFT; - /* Enable BC and horizontal clamp calculation paramaters */ + /* Enable BC and horizontal clamp calculation parameters */ val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) << ISIF_HORZ_BC_MODE_SHIFT); @@ -712,7 +712,7 @@ static void isif_config_bclamp(struct vpfe_isif_device *isif, isif_write(isif->isif_cfg.base_addr, val, CLHWIN2); } - /* vertical clamp calculation paramaters */ + /* vertical clamp calculation parameters */ /* OB H Valid */ val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index 6098f43ac51b..9d726298b406 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -1284,7 +1284,7 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) * @cfg: V4L2 subdev pad config * @pad: pad number. * @which: wanted subdev format. - * Retun wanted mbus frame format. + * Return wanted mbus frame format. */ static struct v4l2_mbus_framefmt * __resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, @@ -1785,7 +1785,7 @@ void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) /* * vpfe_resizer_register_entities() - Register entity - * @resizer - pointer to resizer devive. + * @resizer - pointer to resizer device. * @vdev: pointer to v4l2 device structure. */ int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index 34d63c2e9199..57b93605bc58 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -528,7 +528,7 @@ static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, * @vpfe_dev - ptr to vpfe capture device * @pdev - pointer to platform device * - * intialize all v4l2 subdevs and media entities + * initialize all v4l2 subdevs and media entities */ static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, struct platform_device *pdev) diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig index bfc17de56b17..36b276ea2ecc 100644 --- a/drivers/staging/media/imx/Kconfig +++ b/drivers/staging/media/imx/Kconfig @@ -11,7 +11,7 @@ config VIDEO_IMX_MEDIA driver for the i.MX5/6 SOC. if VIDEO_IMX_MEDIA -menu "i.MX5/6 Media Sub devices" +menu "i.MX5/6/7 Media Sub devices" config VIDEO_IMX_CSI tristate "i.MX5/6 Camera Sensor Interface driver" @@ -20,5 +20,12 @@ config VIDEO_IMX_CSI ---help--- A video4linux camera sensor interface driver for i.MX5/6. +config VIDEO_IMX7_CSI + tristate "i.MX7 Camera Sensor Interface driver" + depends on VIDEO_IMX_MEDIA && VIDEO_DEV && I2C + default y + help + Enable support for video4linux camera sensor interface driver for + i.MX7. endmenu endif diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 698a4210316e..d2d909a36239 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o +imx-media-objs += imx-media-dev-common.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 @@ -11,3 +12,6 @@ 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 + +obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o +obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index aeeb15494a49..6f29b5ca5324 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -45,3 +45,12 @@ Which means a port must not contain mixed-use endpoints, they must all refer to media links between V4L2 subdevices. + +- i.MX7: all of the above, since it uses the imx media core + +- i.MX7: use Frame Interval Monitor + +- i.MX7: runtime testing with parallel sensor, links setup and streaming + +- i.MX7: runtime testing with different formats, for the time only 10-bit bayer + is tested diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c index cfdd4900a3be..765919487a73 100644 --- a/drivers/staging/media/imx/imx-ic-common.c +++ b/drivers/staging/media/imx/imx-ic-common.c @@ -41,13 +41,13 @@ static int imx_ic_probe(struct platform_device *pdev) pdata = priv->dev->platform_data; priv->ipu_id = pdata->ipu_id; switch (pdata->grp_id) { - case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: priv->task_id = IC_TASK_PRP; break; - case IMX_MEDIA_GRP_ID_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: priv->task_id = IC_TASK_ENCODER; break; - case IMX_MEDIA_GRP_ID_IC_PRPVF: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: priv->task_id = IC_TASK_VIEWFINDER; break; default: diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 98923fc844ce..3d43cdcb4bb9 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -77,7 +77,7 @@ static int prp_start(struct prp_priv *priv) 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); + src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC); ipu_set_ic_src_mux(priv->ipu, priv->csi_id, src_is_vdic); @@ -237,8 +237,8 @@ static int prp_link_setup(struct media_entity *entity, ret = -EBUSY; goto out; } - if (priv->sink_sd_prpenc && (remote_sd->grp_id & - IMX_MEDIA_GRP_ID_VDIC)) { + if (priv->sink_sd_prpenc && + (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) { ret = -EINVAL; goto out; } @@ -259,7 +259,7 @@ static int prp_link_setup(struct media_entity *entity, goto out; } if (priv->src_sd && (priv->src_sd->grp_id & - IMX_MEDIA_GRP_ID_VDIC)) { + IMX_MEDIA_GRP_ID_IPU_VDIC)) { ret = -EINVAL; goto out; } @@ -309,13 +309,13 @@ static int prp_link_validate(struct v4l2_subdev *sd, return ret; csi = imx_media_find_upstream_subdev(priv->md, &ic_priv->sd.entity, - IMX_MEDIA_GRP_ID_CSI); + IMX_MEDIA_GRP_ID_IPU_CSI); if (IS_ERR(csi)) csi = NULL; mutex_lock(&priv->lock); - if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC) { + if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) { /* * the ->PRPENC link cannot be enabled if the source * is the VDIC @@ -334,10 +334,10 @@ static int prp_link_validate(struct v4l2_subdev *sd, if (csi) { switch (csi->grp_id) { - case IMX_MEDIA_GRP_ID_CSI0: + case IMX_MEDIA_GRP_ID_IPU_CSI0: priv->csi_id = 0; break; - case IMX_MEDIA_GRP_ID_CSI1: + case IMX_MEDIA_GRP_ID_IPU_CSI1: priv->csi_id = 1; break; default: @@ -422,9 +422,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRP_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..5c8e6ad8c025 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -48,7 +48,7 @@ #define MAX_W_SRC 1024 #define MAX_H_SRC 1024 -#define W_ALIGN_SRC 4 /* multiple of 16 pixels */ +#define W_ALIGN_SRC 1 /* multiple of 2 pixels */ #define H_ALIGN_SRC 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -354,20 +358,30 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_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; + image.rect = vdev->compose; + + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -378,15 +392,25 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* + * Skip writing U and V components to odd rows in the output + * channels for planar 4:2:0 (but not when enabling IDMAC + * interweaving, they are incompatible). + */ + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } @@ -409,10 +433,12 @@ static int prp_setup_channel(struct prp_priv *priv, 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); + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, @@ -680,12 +706,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, + "upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -717,6 +754,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -838,8 +881,7 @@ static void prp_try_fmt(struct prp_priv *priv, 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; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); @@ -870,6 +912,7 @@ static int prp_set_fmt(struct v4l2_subdev *sd, const struct imx_media_pixfmt *cc; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect vdev_compose; int ret = 0; if (sdformat->pad >= PRPENCVF_NUM_PADS) @@ -911,11 +954,11 @@ static int prp_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[PRPENCVF_SRC_PAD], priv->cc[PRPENCVF_SRC_PAD]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: @@ -1148,15 +1191,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) 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) @@ -1189,9 +1223,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRPENCVF_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..9703c85b19c4 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -203,21 +203,14 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, return 0; } -static int capture_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) +static int __capture_try_fmt_vid_cap(struct capture_priv *priv, + struct v4l2_subdev_format *fmt_src, + struct v4l2_format *f, + struct v4l2_rect *compose) { - 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); + cc_src = imx_media_find_ipu_format(fmt_src->format.code, CS_SEL_ANY); if (cc_src) { u32 fourcc, cs_sel; @@ -231,7 +224,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = imx_media_find_format(fourcc, cs_sel, false); } } else { - cc_src = imx_media_find_mbus_format(fmt_src.format.code, + cc_src = imx_media_find_mbus_format(fmt_src->format.code, CS_SEL_ANY, true); if (WARN_ON(!cc_src)) return -EINVAL; @@ -239,15 +232,48 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = cc_src; } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); + /* allow IDMAC interweave but enforce field order from source */ + if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) { + switch (fmt_src->format.field) { + case V4L2_FIELD_SEQ_TB: + fmt_src->format.field = V4L2_FIELD_INTERLACED_TB; + break; + case V4L2_FIELD_SEQ_BT: + fmt_src->format.field = V4L2_FIELD_INTERLACED_BT; + break; + default: + break; + } + } + + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose, + &fmt_src->format, cc); 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; + 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; + + return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL); +} + static int capture_s_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; + struct v4l2_rect compose; int ret; if (vb2_is_busy(&priv->q)) { @@ -255,13 +281,20 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, return -EBUSY; } - ret = capture_try_fmt_vid_cap(file, priv, f); + 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; + + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose); 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); + priv->vdev.compose = compose; return 0; } @@ -290,6 +323,36 @@ static int capture_s_std(struct file *file, void *fh, v4l2_std_id std) return v4l2_subdev_call(priv->src_sd, video, s_std, std); } +static int capture_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct capture_priv *priv = video_drvdata(file); + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* The compose rectangle is fixed to the source format. */ + s->r = priv->vdev.compose; + break; + case V4L2_SEL_TGT_COMPOSE_PADDED: + /* + * The hardware writes with a configurable but fixed DMA burst + * size. If the source format width is not burst size aligned, + * the written frame contains padding to the right. + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = priv->vdev.fmt.fmt.pix.width; + s->r.height = priv->vdev.fmt.fmt.pix.height; + break; + default: + return -EINVAL; + } + + return 0; +} + static int capture_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { @@ -335,6 +398,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -350,6 +428,8 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_g_std = capture_g_std, .vidioc_s_std = capture_s_std, + .vidioc_g_selection = capture_g_selection, + .vidioc_g_parm = capture_g_parm, .vidioc_s_parm = capture_s_parm, @@ -362,6 +442,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -572,7 +655,8 @@ static struct video_device capture_videodev = { }; void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - struct v4l2_pix_format *pix) + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose) { struct capture_priv *priv = to_capture_priv(vdev); @@ -580,6 +664,7 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, priv->vdev.fmt.fmt.pix = *pix; priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY, true); + priv->vdev.compose = *compose; mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format); @@ -685,7 +770,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) } vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, + imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &vdev->compose, &fmt_src.format, NULL); vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4223f8d418ae..3b7517348666 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -41,7 +41,7 @@ #define MIN_H 144 #define MAX_W 4096 #define MAX_H 4096 -#define W_ALIGN 4 /* multiple of 16 pixels */ +#define W_ALIGN 1 /* multiple of 2 pixels */ #define H_ALIGN 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); + if (priv->interweave_swap) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); } @@ -398,23 +402,24 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; + bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; dma_addr_t phys[2]; - bool passthrough; u32 burst_size; int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; incc = priv->cc[CSI_SINK_PAD]; + outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC]; 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; + image.rect = vdev->compose; csi_idmac_setup_vb2_buf(priv, phys); @@ -424,6 +429,16 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); passthrough_cycles = 1; + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; + switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: @@ -442,13 +457,18 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* + * Skip writing U and V components to odd rows (but not + * when enabling IDMAC interweaving, they are incompatible). + */ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: @@ -471,6 +491,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) } if (passthrough) { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.phys0 += image.pix.bytesperline; + image.phys1 += image.pix.bytesperline; + } + ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width * passthrough_cycles, image.rect.height); @@ -480,6 +506,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_cpmem_set_format_passthrough(priv->idmac_ch, passthrough_bits); } else { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + ret = ipu_cpmem_set_image(priv->idmac_ch, &image); if (ret) goto unsetup_vb2; @@ -509,10 +540,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_smfc_set_burstsize(priv->smfc, burst_size); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field)) + if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); @@ -629,7 +662,7 @@ out_put_ipu: return ret; } -static void csi_idmac_stop(struct csi_priv *priv) +static void csi_idmac_wait_last_eof(struct csi_priv *priv) { unsigned long flags; int ret; @@ -646,7 +679,10 @@ static void csi_idmac_stop(struct csi_priv *priv) &priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); +} +static void csi_idmac_stop(struct csi_priv *priv) +{ devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -679,12 +715,7 @@ static int csi_setup(struct csi_priv *priv) priv->upstream_ep.bus.parallel.flags : priv->upstream_ep.bus.mipi_csi2.flags; - /* - * we need to pass input frame to CSI interface, but - * with translated field type from output format - */ if_fmt = *infmt; - if_fmt.field = outfmt->field; crop = priv->crop; /* @@ -702,7 +733,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -722,10 +753,16 @@ static int csi_start(struct csi_priv *priv) output_fi = &priv->frame_interval[priv->active_output_pad]; + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) + return ret; + if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) - return ret; + goto stop_upstream; } ret = csi_setup(priv); @@ -753,11 +790,26 @@ fim_off: idmac_stop: if (priv->dest == IPU_CSI_DEST_IDMAC) csi_idmac_stop(priv); +stop_upstream: + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); return ret; } static void csi_stop(struct csi_priv *priv) { + if (priv->dest == IPU_CSI_DEST_IDMAC) + csi_idmac_wait_last_eof(priv); + + /* + * Disable the CSI asap, after syncing with the last EOF. + * Doing so after the IDMA channel is disabled has shown to + * create hard system-wide hangs. + */ + ipu_csi_disable(priv->csi); + + /* stop upstream */ + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -765,8 +817,6 @@ static void csi_stop(struct csi_priv *priv) 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] = { @@ -876,7 +926,10 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case CSI_SINK_PAD: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = *input_fi; /* 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; @@ -927,23 +980,13 @@ static int csi_s_stream(struct v4l2_subdev *sd, int 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); + if (ret) 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: @@ -1001,6 +1044,8 @@ static int csi_link_setup(struct media_entity *entity, v4l2_ctrl_handler_free(&priv->ctrl_hdlr); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); priv->sink = NULL; + /* do not apply IC burst alignment in csi_try_crop */ + priv->active_output_pad = CSI_SRC_PAD_IDMAC; goto out; } @@ -1029,10 +1074,10 @@ static int csi_link_setup(struct media_entity *entity, remote_sd = media_entity_to_v4l2_subdev(remote->entity); switch (remote_sd->grp_id) { - case IMX_MEDIA_GRP_ID_VDIC: + case IMX_MEDIA_GRP_ID_IPU_VDIC: priv->dest = IPU_CSI_DEST_VDIC; break; - case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: priv->dest = IPU_CSI_DEST_IC; break; default: @@ -1137,12 +1182,21 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + 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; + if (priv->active_output_pad == CSI_SRC_PAD_DIRECT) + crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */ + else + crop->width &= ~0x1; /* multiple of 2 pixels */ + + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; /* * FIXME: not sure why yet, but on interlaced bt.656, @@ -1153,12 +1207,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_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; + crop->height = in_height; + crop->top = (in_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; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1308,6 +1362,49 @@ out: return ret; } +static void csi_try_field(struct csi_priv *priv, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct v4l2_mbus_framefmt *infmt = + __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + + /* no restrictions on sink pad field type */ + if (sdformat->pad == CSI_SINK_PAD) + return; + + switch (infmt->field) { + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + /* + * If the user requests sequential at the source pad, + * allow it (along with possibly inverting field order). + * Otherwise passthrough the field type. + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = infmt->field; + break; + case V4L2_FIELD_ALTERNATE: + /* + * This driver does not support alternate field mode, and + * the CSI captures a whole frame, so the CSI never presents + * alternate mode at its source pads. If user has not + * already requested sequential, translate ALTERNATE at + * sink pad to SEQ_TB or SEQ_BT at the source pad depending + * on input height (assume NTSC BT order if 480 total active + * frame lines, otherwise PAL TB order). + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = (infmt->height == 480 / 2) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB; + break; + default: + /* Passthrough for all other input field types */ + sdformat->format.field = infmt->field; + break; + } +} + static void csi_try_fmt(struct csi_priv *priv, struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, @@ -1347,42 +1444,20 @@ static void csi_try_fmt(struct csi_priv *priv, } } - 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; - } + csi_try_field(priv, cfg, sdformat); /* 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, upstream_ep); - 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) { @@ -1393,9 +1468,25 @@ static void csi_try_fmt(struct csi_priv *priv, sdformat->format.code = (*cc)->codes[0]; } + csi_try_field(priv, cfg, sdformat); + imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } @@ -1411,6 +1502,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; + struct v4l2_rect vdev_compose; int ret; if (sdformat->pad >= CSI_NUM_PADS) @@ -1466,11 +1558,11 @@ static int csi_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate IDMAC output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &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); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: @@ -1502,6 +1594,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; @@ -1787,7 +1881,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) @@ -1864,6 +1958,8 @@ static int imx_csi_probe(struct platform_device *pdev) priv->csi_id = pdata->csi; priv->smfc_id = (priv->csi_id == 0) ? 0 : 2; + priv->active_output_pad = CSI_SRC_PAD_IDMAC; + timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0); spin_lock_init(&priv->irqlock); @@ -1877,7 +1973,7 @@ static int imx_csi_probe(struct platform_device *pdev) 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_IPU_CSI1 : IMX_MEDIA_GRP_ID_IPU_CSI0; imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), priv->sd.grp_id, ipu_get_num(priv->ipu)); diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c new file mode 100644 index 000000000000..910594125889 --- /dev/null +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Media Controller Driver for Freescale common i.MX5/6/7 SOC + * + * Copyright (c) 2019 Linaro Ltd + * Copyright (c) 2016 Mentor Graphics Inc. + */ + +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include "imx-media.h" + +static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { + .bound = imx_media_subdev_bound, + .complete = imx_media_probe_complete, +}; + +static const struct media_device_ops imx_media_md_ops = { + .link_notify = imx_media_link_notify, +}; + +struct imx_media_dev *imx_media_dev_init(struct device *dev) +{ + struct imx_media_dev *imxmd; + int ret; + + imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); + if (!imxmd) + return ERR_PTR(-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; + imxmd->v4l2_dev.notify = imx_media_notify; + 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); + + INIT_LIST_HEAD(&imxmd->vdev_list); + + v4l2_async_notifier_init(&imxmd->notifier); + + return imxmd; + +cleanup: + media_device_cleanup(&imxmd->md); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(imx_media_dev_init); + +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) +{ + int ret; + + /* no subdevs? just bail */ + if (list_empty(&imxmd->notifier.asd_list)) { + v4l2_err(&imxmd->v4l2_dev, "no subdevs\n"); + return -ENODEV; + } + + /* prepare the async subdev notifier and register it */ + imxmd->notifier.ops = &imx_media_subdev_ops; + ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, + &imxmd->notifier); + if (ret) { + v4l2_err(&imxmd->v4l2_dev, + "v4l2_async_notifier_register failed with %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imx_media_dev_notifier_register); diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..28a3d23aad5b 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -116,16 +116,16 @@ static int imx_media_get_ipu(struct imx_media_dev *imxmd, } /* async subdev bound notifier */ -static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +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); int ret = 0; mutex_lock(&imxmd->mutex); - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) { + if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) { ret = imx_media_get_ipu(imxmd, sd); if (ret) goto out; @@ -149,13 +149,13 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier) list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { switch (sd->grp_id) { - case IMX_MEDIA_GRP_ID_VDIC: - case IMX_MEDIA_GRP_ID_IC_PRP: - case IMX_MEDIA_GRP_ID_IC_PRPENC: - case IMX_MEDIA_GRP_ID_IC_PRPVF: - case IMX_MEDIA_GRP_ID_CSI0: - case IMX_MEDIA_GRP_ID_CSI1: - ret = imx_media_create_internal_links(imxmd, sd); + case IMX_MEDIA_GRP_ID_IPU_VDIC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: + case IMX_MEDIA_GRP_ID_IPU_CSI0: + case IMX_MEDIA_GRP_ID_IPU_CSI1: + ret = imx_media_create_ipu_internal_links(imxmd, sd); if (ret) return ret; /* @@ -163,9 +163,13 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier) * internal entities, so create the external links * to the CSI sink pads. */ - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) + if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) imx_media_create_csi_of_links(imxmd, sd); break; + case IMX_MEDIA_GRP_ID_CSI: + imx_media_create_csi_of_links(imxmd, sd); + + break; default: /* * if this subdev has fwnode links, create media @@ -302,7 +306,7 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) } /* async subdev complete notifier */ -static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) +int imx_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; @@ -326,11 +330,6 @@ unlock: return media_device_register(&imxmd->md); } -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { - .bound = imx_media_subdev_bound, - .complete = imx_media_probe_complete, -}; - /* * adds controls to a video device from an entity subdevice. * Continues upstream from the entity's sink pads. @@ -374,8 +373,8 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd, return ret; } -static int imx_media_link_notify(struct media_link *link, u32 flags, - unsigned int notification) +int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification) { struct media_entity *source = link->source->entity; struct imx_media_pad_vdev *pad_vdev; @@ -438,9 +437,27 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, return ret; } -static const struct media_device_ops imx_media_md_ops = { - .link_notify = imx_media_link_notify, -}; +void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} static int imx_media_probe(struct platform_device *pdev) { @@ -449,76 +466,37 @@ static int imx_media_probe(struct platform_device *pdev) struct imx_media_dev *imxmd; int ret; - imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); - if (!imxmd) - return -ENOMEM; - - dev_set_drvdata(dev, imxmd); - - strscpy(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; - strscpy(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); - - INIT_LIST_HEAD(&imxmd->vdev_list); - - v4l2_async_notifier_init(&imxmd->notifier); + imxmd = imx_media_dev_init(dev); + if (IS_ERR(imxmd)) + return PTR_ERR(imxmd); ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_of_subdevs failed with %d\n", ret); - goto notifier_cleanup; + goto cleanup; } ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); - goto notifier_cleanup; - } - - /* no subdevs? just bail */ - if (list_empty(&imxmd->notifier.asd_list)) { - ret = -ENODEV; - goto notifier_cleanup; + goto cleanup; } - /* prepare the async subdev notifier and register it */ - imxmd->notifier.ops = &imx_media_subdev_ops; - ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, - &imxmd->notifier); - if (ret) { - v4l2_err(&imxmd->v4l2_dev, - "v4l2_async_notifier_register failed with %d\n", ret); + ret = imx_media_dev_notifier_register(imxmd); + if (ret) goto del_int; - } return 0; del_int: imx_media_remove_internal_subdevs(imxmd); -notifier_cleanup: +cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); -cleanup: media_device_cleanup(&imxmd->md); + return ret; } @@ -532,8 +510,8 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_remove_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); - v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); + v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); return 0; diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index 0fdc45dbfb76..5e10d95e5529 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -30,32 +30,32 @@ static const struct internal_subdev_id { } isd_id[num_isd] = { [isd_csi0] = { .index = isd_csi0, - .grp_id = IMX_MEDIA_GRP_ID_CSI0, + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0, .name = "imx-ipuv3-csi", }, [isd_csi1] = { .index = isd_csi1, - .grp_id = IMX_MEDIA_GRP_ID_CSI1, + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1, .name = "imx-ipuv3-csi", }, [isd_vdic] = { .index = isd_vdic, - .grp_id = IMX_MEDIA_GRP_ID_VDIC, + .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC, .name = "imx-ipuv3-vdic", }, [isd_ic_prp] = { .index = isd_ic_prp, - .grp_id = IMX_MEDIA_GRP_ID_IC_PRP, + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP, .name = "imx-ipuv3-ic", }, [isd_ic_prpenc] = { .index = isd_ic_prpenc, - .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC, + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC, .name = "imx-ipuv3-ic", }, [isd_ic_prpvf] = { .index = isd_ic_prpvf, - .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF, + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF, .name = "imx-ipuv3-ic", }, }; @@ -229,8 +229,8 @@ static int create_ipu_internal_link(struct imx_media_dev *imxmd, return ret; } -int imx_media_create_internal_links(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd) +int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) { const struct internal_subdev *intsd; const struct internal_pad *intpad; @@ -312,8 +312,8 @@ static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id) * of_parse_subdev(). */ switch (isd->id->grp_id) { - case IMX_MEDIA_GRP_ID_CSI0: - case IMX_MEDIA_GRP_ID_CSI1: + case IMX_MEDIA_GRP_ID_IPU_CSI0: + case IMX_MEDIA_GRP_ID_IPU_CSI1: ret = 0; break; default: diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index a01327f6e045..03446335ac03 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,7 +20,8 @@ #include <video/imx-ipu-v3.h> #include "imx-media.h" -static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) +int imx_media_of_add_csi(struct imx_media_dev *imxmd, + struct device_node *csi_np) { int ret; @@ -45,6 +46,7 @@ static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) return 0; } +EXPORT_SYMBOL_GPL(imx_media_of_add_csi); int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, struct device_node *np) @@ -57,7 +59,7 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, if (!csi_np) break; - ret = of_add_csi(imxmd, csi_np); + ret = imx_media_of_add_csi(imxmd, csi_np); of_node_put(csi_np); if (ret) return ret; diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 0eaa353d5cb3..1c63a2765a81 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -577,9 +577,11 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, 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, + struct v4l2_rect *compose, + const struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { + u32 width; u32 stride; if (!cc) { @@ -602,9 +604,16 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false); } - stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3; + /* Round up width for minimum burst size */ + width = round_up(mbus->width, 8); - pix->width = mbus->width; + /* Round up stride for IDMAC line start address alignment */ + if (cc->planar) + stride = round_up(width, 16); + else + stride = round_up((width * cc->bpp) >> 3, 8); + + pix->width = width; pix->height = mbus->height; pix->pixelformat = cc->fourcc; pix->colorspace = mbus->colorspace; @@ -613,7 +622,19 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->quantization = mbus->quantization; pix->field = mbus->field; pix->bytesperline = stride; - pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3; + pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : + stride * pix->height; + + /* + * set capture compose rectangle, which is fixed to the + * source subdevice mbus format. + */ + if (compose) { + compose->left = 0; + compose->top = 0; + compose->width = mbus->width; + compose->height = mbus->height; + } return 0; } @@ -626,13 +647,11 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, memset(image, 0, sizeof(*image)); - ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL); + ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, &image->rect, + 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); @@ -696,20 +715,20 @@ 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; + case IMX_MEDIA_GRP_ID_IPU_CSI0...IMX_MEDIA_GRP_ID_IPU_CSI1: + id = (grp_id >> IMX_MEDIA_GRP_ID_IPU_CSI_BIT) - 1; snprintf(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id); break; - case IMX_MEDIA_GRP_ID_VDIC: + case IMX_MEDIA_GRP_ID_IPU_VDIC: snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1); break; - case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1); break; - case IMX_MEDIA_GRP_ID_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1); break; - case IMX_MEDIA_GRP_ID_IC_PRPVF: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: snprintf(sd_name, sz, "ipu%d_ic_prpvf", ipu_id + 1); break; default: diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..2808662e2597 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, 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_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: 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); @@ -263,10 +255,10 @@ static int setup_vdi_channel(struct vdic_priv *priv, memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; + image.rect = vdev->compose; /* one field to VDIC channels */ image.pix.height /= 2; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect.height /= 2; image.phys0 = phys0; image.phys1 = phys1; @@ -826,7 +818,10 @@ static int vdic_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case VDIC_SINK_PAD_DIRECT: case VDIC_SINK_PAD_IDMAC: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = priv->frame_interval[fi->pad]; /* Reset output interval */ *output_fi = fi->interval; if (priv->csi_direct) diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index bc7feb81937c..ae964c8d5be1 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -80,6 +80,8 @@ struct imx_media_video_dev { /* the user format */ struct v4l2_format fmt; + /* the compose rectangle */ + struct v4l2_rect compose; const struct imx_media_pixfmt *cc; /* links this vdev to master list */ @@ -178,7 +180,8 @@ 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, + struct v4l2_rect *compose, + const 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); @@ -226,6 +229,18 @@ int imx_media_add_async_subdev(struct imx_media_dev *imxmd, struct fwnode_handle *fwnode, struct platform_device *pdev); +int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd); +int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification); +void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg); +int imx_media_probe_complete(struct v4l2_async_notifier *notifier); + +struct imx_media_dev *imx_media_dev_init(struct device *dev); +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd); + /* imx-media-fim.c */ struct imx_media_fim; void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp); @@ -238,8 +253,8 @@ void imx_media_fim_free(struct imx_media_fim *fim); /* imx-media-internal-sd.c */ int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd); -int imx_media_create_internal_links(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd); +int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd); void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd); /* imx-media-of.c */ @@ -249,6 +264,8 @@ int imx_media_create_of_links(struct imx_media_dev *imxmd, struct v4l2_subdev *sd); int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, struct v4l2_subdev *csi); +int imx_media_of_add_csi(struct imx_media_dev *imxmd, + struct device_node *csi_np); /* imx-media-capture.c */ struct imx_media_video_dev * @@ -259,18 +276,20 @@ 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); + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose); void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ -#define IMX_MEDIA_GRP_ID_CSI2 BIT(8) -#define IMX_MEDIA_GRP_ID_CSI_BIT 9 -#define IMX_MEDIA_GRP_ID_CSI (0x3 << IMX_MEDIA_GRP_ID_CSI_BIT) -#define IMX_MEDIA_GRP_ID_CSI0 BIT(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 BIT(11) -#define IMX_MEDIA_GRP_ID_IC_PRP BIT(12) -#define IMX_MEDIA_GRP_ID_IC_PRPENC BIT(13) -#define IMX_MEDIA_GRP_ID_IC_PRPVF BIT(14) +#define IMX_MEDIA_GRP_ID_CSI2 BIT(8) +#define IMX_MEDIA_GRP_ID_CSI BIT(9) +#define IMX_MEDIA_GRP_ID_IPU_CSI_BIT 10 +#define IMX_MEDIA_GRP_ID_IPU_CSI (0x3 << IMX_MEDIA_GRP_ID_IPU_CSI_BIT) +#define IMX_MEDIA_GRP_ID_IPU_CSI0 BIT(IMX_MEDIA_GRP_ID_IPU_CSI_BIT) +#define IMX_MEDIA_GRP_ID_IPU_CSI1 (2 << IMX_MEDIA_GRP_ID_IPU_CSI_BIT) +#define IMX_MEDIA_GRP_ID_IPU_VDIC BIT(12) +#define IMX_MEDIA_GRP_ID_IPU_IC_PRP BIT(13) +#define IMX_MEDIA_GRP_ID_IPU_IC_PRPENC BIT(14) +#define IMX_MEDIA_GRP_ID_IPU_IC_PRPVF BIT(15) #endif diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c new file mode 100644 index 000000000000..3fba7c27c0ec --- /dev/null +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -0,0 +1,1369 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Capture CSI Subdev for Freescale i.MX7 SOC + * + * Copyright (c) 2019 Linaro Ltd + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gcd.h> +#include <linux/interrupt.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.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 <media/imx.h> +#include "imx-media.h" + +#define IMX7_CSI_PAD_SINK 0 +#define IMX7_CSI_PAD_SRC 1 +#define IMX7_CSI_PADS_NUM 2 + +/* reset values */ +#define CSICR1_RESET_VAL 0x40000800 +#define CSICR2_RESET_VAL 0x0 +#define CSICR3_RESET_VAL 0x0 + +/* csi control reg 1 */ +#define BIT_SWAP16_EN BIT(31) +#define BIT_EXT_VSYNC BIT(30) +#define BIT_EOF_INT_EN BIT(29) +#define BIT_PRP_IF_EN BIT(28) +#define BIT_CCIR_MODE BIT(27) +#define BIT_COF_INT_EN BIT(26) +#define BIT_SF_OR_INTEN BIT(25) +#define BIT_RF_OR_INTEN BIT(24) +#define BIT_SFF_DMA_DONE_INTEN BIT(22) +#define BIT_STATFF_INTEN BIT(21) +#define BIT_FB2_DMA_DONE_INTEN BIT(20) +#define BIT_FB1_DMA_DONE_INTEN BIT(19) +#define BIT_RXFF_INTEN BIT(18) +#define BIT_SOF_POL BIT(17) +#define BIT_SOF_INTEN BIT(16) +#define BIT_MCLKDIV (0xF << 12) +#define BIT_HSYNC_POL BIT(11) +#define BIT_CCIR_EN BIT(10) +#define BIT_MCLKEN BIT(9) +#define BIT_FCC BIT(8) +#define BIT_PACK_DIR BIT(7) +#define BIT_CLR_STATFIFO BIT(6) +#define BIT_CLR_RXFIFO BIT(5) +#define BIT_GCLK_MODE BIT(4) +#define BIT_INV_DATA BIT(3) +#define BIT_INV_PCLK BIT(2) +#define BIT_REDGE BIT(1) +#define BIT_PIXEL_BIT BIT(0) + +#define SHIFT_MCLKDIV 12 + +/* control reg 3 */ +#define BIT_FRMCNT (0xFFFF << 16) +#define BIT_FRMCNT_RST BIT(15) +#define BIT_DMA_REFLASH_RFF BIT(14) +#define BIT_DMA_REFLASH_SFF BIT(13) +#define BIT_DMA_REQ_EN_RFF BIT(12) +#define BIT_DMA_REQ_EN_SFF BIT(11) +#define BIT_STATFF_LEVEL (0x7 << 8) +#define BIT_HRESP_ERR_EN BIT(7) +#define BIT_RXFF_LEVEL (0x7 << 4) +#define BIT_TWO_8BIT_SENSOR BIT(3) +#define BIT_ZERO_PACK_EN BIT(2) +#define BIT_ECC_INT_EN BIT(1) +#define BIT_ECC_AUTO_EN BIT(0) + +#define SHIFT_FRMCNT 16 +#define SHIFT_RXFIFO_LEVEL 4 + +/* csi status reg */ +#define BIT_ADDR_CH_ERR_INT BIT(28) +#define BIT_FIELD0_INT BIT(27) +#define BIT_FIELD1_INT BIT(26) +#define BIT_SFF_OR_INT BIT(25) +#define BIT_RFF_OR_INT BIT(24) +#define BIT_DMA_TSF_DONE_SFF BIT(22) +#define BIT_STATFF_INT BIT(21) +#define BIT_DMA_TSF_DONE_FB2 BIT(20) +#define BIT_DMA_TSF_DONE_FB1 BIT(19) +#define BIT_RXFF_INT BIT(18) +#define BIT_EOF_INT BIT(17) +#define BIT_SOF_INT BIT(16) +#define BIT_F2_INT BIT(15) +#define BIT_F1_INT BIT(14) +#define BIT_COF_INT BIT(13) +#define BIT_HRESP_ERR_INT BIT(7) +#define BIT_ECC_INT BIT(1) +#define BIT_DRDY BIT(0) + +/* csi control reg 18 */ +#define BIT_CSI_HW_ENABLE BIT(31) +#define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25) +#define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25) +#define BIT_MIPI_DATA_FORMAT_RAW12 (0x2c << 25) +#define BIT_MIPI_DATA_FORMAT_RAW14 (0x2d << 25) +#define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25) +#define BIT_MIPI_DATA_FORMAT_MASK (0x3F << 25) +#define BIT_MIPI_DATA_FORMAT_OFFSET 25 +#define BIT_DATA_FROM_MIPI BIT(22) +#define BIT_MIPI_YU_SWAP BIT(21) +#define BIT_MIPI_DOUBLE_CMPNT BIT(20) +#define BIT_BASEADDR_CHG_ERR_EN BIT(9) +#define BIT_BASEADDR_SWITCH_SEL BIT(5) +#define BIT_BASEADDR_SWITCH_EN BIT(4) +#define BIT_PARALLEL24_EN BIT(3) +#define BIT_DEINTERLACE_EN BIT(2) +#define BIT_TVDECODER_IN_EN BIT(1) +#define BIT_NTSC_EN BIT(0) + +#define CSI_MCLK_VF 1 +#define CSI_MCLK_ENC 2 +#define CSI_MCLK_RAW 4 +#define CSI_MCLK_I2C 8 + +#define CSI_CSICR1 0x0 +#define CSI_CSICR2 0x4 +#define CSI_CSICR3 0x8 +#define CSI_STATFIFO 0xC +#define CSI_CSIRXFIFO 0x10 +#define CSI_CSIRXCNT 0x14 +#define CSI_CSISR 0x18 + +#define CSI_CSIDBG 0x1C +#define CSI_CSIDMASA_STATFIFO 0x20 +#define CSI_CSIDMATS_STATFIFO 0x24 +#define CSI_CSIDMASA_FB1 0x28 +#define CSI_CSIDMASA_FB2 0x2C +#define CSI_CSIFBUF_PARA 0x30 +#define CSI_CSIIMAG_PARA 0x34 + +#define CSI_CSICR18 0x48 +#define CSI_CSICR19 0x4c + +static const char * const imx7_csi_clk_id[] = {"axi", "dcic", "mclk"}; + +struct imx7_csi { + struct device *dev; + struct v4l2_subdev sd; + struct imx_media_video_dev *vdev; + struct imx_media_dev *imxmd; + struct media_pad pad[IMX7_CSI_PADS_NUM]; + + /* lock to protect members below */ + struct mutex lock; + /* lock to protect irq handler when stop streaming */ + spinlock_t irqlock; + + struct v4l2_subdev *src_sd; + + struct media_entity *sink; + + struct v4l2_fwnode_endpoint upstream_ep; + + struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM]; + const struct imx_media_pixfmt *cc[IMX7_CSI_PADS_NUM]; + struct v4l2_fract frame_interval[IMX7_CSI_PADS_NUM]; + + struct v4l2_ctrl_handler ctrl_hdlr; + + void __iomem *regbase; + int irq; + + int num_clks; + struct clk_bulk_data *clks; + + /* 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 buf_num; + u32 frame_sequence; + + bool last_eof; + bool is_init; + bool is_streaming; + bool is_csi2; + + struct completion last_eof_completion; +}; + +#define imx7_csi_reg_read(_csi, _offset) \ + __raw_readl((_csi)->regbase + (_offset)) +#define imx7_csi_reg_write(_csi, _val, _offset) \ + __raw_writel(_val, (_csi)->regbase + (_offset)) + +static void imx7_csi_clk_enable(struct imx7_csi *csi) +{ + int ret; + + ret = clk_bulk_prepare_enable(csi->num_clks, csi->clks); + if (ret < 0) + dev_err(csi->dev, "failed to enable clocks\n"); +} + +static void imx7_csi_clk_disable(struct imx7_csi *csi) +{ + clk_bulk_disable_unprepare(csi->num_clks, csi->clks); +} + +static void imx7_csi_hw_reset(struct imx7_csi *csi) +{ + imx7_csi_reg_write(csi, + imx7_csi_reg_read(csi, CSI_CSICR3) | BIT_FRMCNT_RST, + CSI_CSICR3); + + imx7_csi_reg_write(csi, CSICR1_RESET_VAL, CSI_CSICR1); + imx7_csi_reg_write(csi, CSICR2_RESET_VAL, CSI_CSICR2); + imx7_csi_reg_write(csi, CSICR3_RESET_VAL, CSI_CSICR3); +} + +static unsigned long imx7_csi_irq_clear(struct imx7_csi *csi) +{ + unsigned long isr; + + isr = imx7_csi_reg_read(csi, CSI_CSISR); + imx7_csi_reg_write(csi, isr, CSI_CSISR); + + return isr; +} + +static void imx7_csi_init_interface(struct imx7_csi *csi) +{ + unsigned int val = 0; + unsigned int imag_para; + + val = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL | + BIT_FCC | 1 << SHIFT_MCLKDIV | BIT_MCLKEN; + imx7_csi_reg_write(csi, val, CSI_CSICR1); + + imag_para = (800 << 16) | 600; + imx7_csi_reg_write(csi, imag_para, CSI_CSIIMAG_PARA); + + val = BIT_DMA_REFLASH_RFF; + imx7_csi_reg_write(csi, val, CSI_CSICR3); +} + +static void imx7_csi_hw_enable_irq(struct imx7_csi *csi) +{ + unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + + cr1 |= BIT_SOF_INTEN; + cr1 |= BIT_RFF_OR_INT; + + /* still capture needs DMA interrupt */ + cr1 |= BIT_FB1_DMA_DONE_INTEN; + cr1 |= BIT_FB2_DMA_DONE_INTEN; + + cr1 |= BIT_EOF_INT_EN; + + imx7_csi_reg_write(csi, cr1, CSI_CSICR1); +} + +static void imx7_csi_hw_disable_irq(struct imx7_csi *csi) +{ + unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + + cr1 &= ~BIT_SOF_INTEN; + cr1 &= ~BIT_RFF_OR_INT; + cr1 &= ~BIT_FB1_DMA_DONE_INTEN; + cr1 &= ~BIT_FB2_DMA_DONE_INTEN; + cr1 &= ~BIT_EOF_INT_EN; + + imx7_csi_reg_write(csi, cr1, CSI_CSICR1); +} + +static void imx7_csi_hw_enable(struct imx7_csi *csi) +{ + unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18); + + cr |= BIT_CSI_HW_ENABLE; + + imx7_csi_reg_write(csi, cr, CSI_CSICR18); +} + +static void imx7_csi_hw_disable(struct imx7_csi *csi) +{ + unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18); + + cr &= ~BIT_CSI_HW_ENABLE; + + imx7_csi_reg_write(csi, cr, CSI_CSICR18); +} + +static void imx7_csi_dma_reflash(struct imx7_csi *csi) +{ + unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR18); + + cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); + cr3 |= BIT_DMA_REFLASH_RFF; + imx7_csi_reg_write(csi, cr3, CSI_CSICR3); +} + +static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi) +{ + unsigned long cr1; + + cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + imx7_csi_reg_write(csi, cr1 & ~BIT_FCC, CSI_CSICR1); + cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1); + + cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1); +} + +static void imx7_csi_buf_stride_set(struct imx7_csi *csi, u32 stride) +{ + imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA); +} + +static void imx7_csi_deinterlace_enable(struct imx7_csi *csi, bool enable) +{ + unsigned long cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); + + if (enable) + cr18 |= BIT_DEINTERLACE_EN; + else + cr18 &= ~BIT_DEINTERLACE_EN; + + imx7_csi_reg_write(csi, cr18, CSI_CSICR18); +} + +static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi) +{ + unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); + unsigned long cr2 = imx7_csi_reg_read(csi, CSI_CSICR2); + + /* Burst Type of DMA Transfer from RxFIFO. INCR16 */ + cr2 |= 0xC0000000; + + cr3 |= BIT_DMA_REQ_EN_RFF; + cr3 |= BIT_HRESP_ERR_EN; + cr3 &= ~BIT_RXFF_LEVEL; + cr3 |= 0x2 << 4; + + imx7_csi_reg_write(csi, cr3, CSI_CSICR3); + imx7_csi_reg_write(csi, cr2, CSI_CSICR2); +} + +static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi) +{ + unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); + + cr3 &= ~BIT_DMA_REQ_EN_RFF; + cr3 &= ~BIT_HRESP_ERR_EN; + imx7_csi_reg_write(csi, cr3, CSI_CSICR3); +} + +static void imx7_csi_set_imagpara(struct imx7_csi *csi, int width, int height) +{ + int imag_para; + int rx_count; + + rx_count = (width * height) >> 2; + imx7_csi_reg_write(csi, rx_count, CSI_CSIRXCNT); + + imag_para = (width << 16) | height; + imx7_csi_reg_write(csi, imag_para, CSI_CSIIMAG_PARA); + + /* reflash the embedded DMA controller */ + imx7_csi_dma_reflash(csi); +} + +static void imx7_csi_sw_reset(struct imx7_csi *csi) +{ + imx7_csi_hw_disable(csi); + + imx7_csi_rx_fifo_clear(csi); + + imx7_csi_dma_reflash(csi); + + usleep_range(2000, 3000); + + imx7_csi_irq_clear(csi); + + imx7_csi_hw_enable(csi); +} + +static void imx7_csi_error_recovery(struct imx7_csi *csi) +{ + imx7_csi_hw_disable(csi); + + imx7_csi_rx_fifo_clear(csi); + + imx7_csi_dma_reflash(csi); + + imx7_csi_hw_enable(csi); +} + +static void imx7_csi_init(struct imx7_csi *csi) +{ + if (csi->is_init) + return; + + imx7_csi_clk_enable(csi); + imx7_csi_hw_reset(csi); + imx7_csi_init_interface(csi); + imx7_csi_dmareq_rff_enable(csi); + + csi->is_init = true; +} + +static void imx7_csi_deinit(struct imx7_csi *csi) +{ + if (!csi->is_init) + return; + + imx7_csi_hw_reset(csi); + imx7_csi_init_interface(csi); + imx7_csi_dmareq_rff_disable(csi); + imx7_csi_clk_disable(csi); + + csi->is_init = false; +} + +static int imx7_csi_get_upstream_endpoint(struct imx7_csi *csi, + struct v4l2_fwnode_endpoint *ep, + bool skip_mux) +{ + struct device_node *endpoint, *port; + struct media_entity *src; + struct v4l2_subdev *sd; + struct media_pad *pad; + + if (!csi->src_sd) + return -EPIPE; + + src = &csi->src_sd->entity; + +skip_video_mux: + /* get source pad of entity directly upstream from src */ + pad = imx_media_find_upstream_pad(csi->imxmd, src, 0); + if (IS_ERR(pad)) + return PTR_ERR(pad); + + sd = media_entity_to_v4l2_subdev(pad->entity); + + /* To get bus type we may need to skip video mux */ + if (skip_mux && src->function == MEDIA_ENT_F_VID_MUX) { + src = &sd->entity; + goto skip_video_mux; + } + + /* + * NOTE: this assumes an OF-graph port id is the same as a + * media pad index. + */ + port = of_graph_get_port_by_id(sd->dev->of_node, pad->index); + if (!port) + return -ENODEV; + + endpoint = of_get_next_child(port, NULL); + of_node_put(port); + if (!endpoint) + return -ENODEV; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep); + of_node_put(endpoint); + + return 0; +} + +static int imx7_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 imx7_csi *csi = v4l2_get_subdevdata(sd); + struct v4l2_subdev *remote_sd; + int ret = 0; + + dev_dbg(csi->dev, "link setup %s -> %s\n", remote->entity->name, + local->entity->name); + + mutex_lock(&csi->lock); + + if (local->flags & MEDIA_PAD_FL_SINK) { + if (!is_media_entity_v4l2_subdev(remote->entity)) { + ret = -EINVAL; + goto unlock; + } + + remote_sd = media_entity_to_v4l2_subdev(remote->entity); + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi->src_sd) { + ret = -EBUSY; + goto unlock; + } + csi->src_sd = remote_sd; + } else { + csi->src_sd = NULL; + } + + goto init; + } + + /* source pad */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi->sink) { + ret = -EBUSY; + goto unlock; + } + csi->sink = remote->entity; + } else { + v4l2_ctrl_handler_free(&csi->ctrl_hdlr); + v4l2_ctrl_handler_init(&csi->ctrl_hdlr, 0); + csi->sink = NULL; + } + +init: + if (csi->sink || csi->src_sd) + imx7_csi_init(csi); + else + imx7_csi_deinit(csi); + +unlock: + mutex_unlock(&csi->lock); + + return ret; +} + +static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct v4l2_fwnode_endpoint upstream_ep = {}; + int ret; + + ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); + if (ret) + return ret; + + ret = imx7_csi_get_upstream_endpoint(csi, &upstream_ep, true); + if (ret) { + v4l2_err(&csi->sd, "failed to find upstream endpoint\n"); + return ret; + } + + mutex_lock(&csi->lock); + + csi->upstream_ep = upstream_ep; + csi->is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2_DPHY); + + mutex_unlock(&csi->lock); + + return 0; +} + +static void imx7_csi_update_buf(struct imx7_csi *csi, dma_addr_t phys, + int buf_num) +{ + if (buf_num == 1) + imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB2); + else + imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB1); +} + +static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi) +{ + struct imx_media_video_dev *vdev = csi->vdev; + struct imx_media_buffer *buf; + struct vb2_buffer *vb2_buf; + dma_addr_t phys[2]; + int i; + + for (i = 0; i < 2; i++) { + buf = imx_media_capture_device_next_buf(vdev); + if (buf) { + csi->active_vb2_buf[i] = buf; + vb2_buf = &buf->vbuf.vb2_buf; + phys[i] = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); + } else { + csi->active_vb2_buf[i] = NULL; + phys[i] = csi->underrun_buf.phys; + } + + imx7_csi_update_buf(csi, phys[i], i); + } +} + +static void imx7_csi_dma_unsetup_vb2_buf(struct imx7_csi *csi, + 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 = csi->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 void imx7_csi_vb2_buf_done(struct imx7_csi *csi) +{ + struct imx_media_video_dev *vdev = csi->vdev; + struct imx_media_buffer *done, *next; + struct vb2_buffer *vb; + dma_addr_t phys; + + done = csi->active_vb2_buf[csi->buf_num]; + if (done) { + done->vbuf.field = vdev->fmt.fmt.pix.field; + done->vbuf.sequence = csi->frame_sequence; + vb = &done->vbuf.vb2_buf; + vb->timestamp = ktime_get_ns(); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } + csi->frame_sequence++; + + /* 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); + csi->active_vb2_buf[csi->buf_num] = next; + } else { + phys = csi->underrun_buf.phys; + csi->active_vb2_buf[csi->buf_num] = NULL; + } + + imx7_csi_update_buf(csi, phys, csi->buf_num); +} + +static irqreturn_t imx7_csi_irq_handler(int irq, void *data) +{ + struct imx7_csi *csi = data; + unsigned long status; + + spin_lock(&csi->irqlock); + + status = imx7_csi_irq_clear(csi); + + if (status & BIT_RFF_OR_INT) { + dev_warn(csi->dev, "Rx fifo overflow\n"); + imx7_csi_error_recovery(csi); + } + + if (status & BIT_HRESP_ERR_INT) { + dev_warn(csi->dev, "Hresponse error detected\n"); + imx7_csi_error_recovery(csi); + } + + if (status & BIT_ADDR_CH_ERR_INT) { + imx7_csi_hw_disable(csi); + + imx7_csi_dma_reflash(csi); + + imx7_csi_hw_enable(csi); + } + + if ((status & BIT_DMA_TSF_DONE_FB1) && + (status & BIT_DMA_TSF_DONE_FB2)) { + /* + * For both FB1 and FB2 interrupter bits set case, + * CSI DMA is work in one of FB1 and FB2 buffer, + * but software can not know the state. + * Skip it to avoid base address updated + * when csi work in field0 and field1 will write to + * new base address. + */ + } else if (status & BIT_DMA_TSF_DONE_FB1) { + csi->buf_num = 0; + } else if (status & BIT_DMA_TSF_DONE_FB2) { + csi->buf_num = 1; + } + + if ((status & BIT_DMA_TSF_DONE_FB1) || + (status & BIT_DMA_TSF_DONE_FB2)) { + imx7_csi_vb2_buf_done(csi); + + if (csi->last_eof) { + complete(&csi->last_eof_completion); + csi->last_eof = false; + } + } + + spin_unlock(&csi->irqlock); + + return IRQ_HANDLED; +} + +static int imx7_csi_dma_start(struct imx7_csi *csi) +{ + struct imx_media_video_dev *vdev = csi->vdev; + struct v4l2_pix_format *out_pix = &vdev->fmt.fmt.pix; + int ret; + + ret = imx_media_alloc_dma_buf(csi->imxmd, &csi->underrun_buf, + out_pix->sizeimage); + if (ret < 0) { + v4l2_warn(&csi->sd, "consider increasing the CMA area\n"); + return ret; + } + + csi->frame_sequence = 0; + csi->last_eof = false; + init_completion(&csi->last_eof_completion); + + imx7_csi_setup_vb2_buf(csi); + + return 0; +} + +static void imx7_csi_dma_stop(struct imx7_csi *csi) +{ + unsigned long timeout_jiffies; + unsigned long flags; + int ret; + + /* mark next EOF interrupt as the last before stream off */ + spin_lock_irqsave(&csi->irqlock, flags); + csi->last_eof = true; + spin_unlock_irqrestore(&csi->irqlock, flags); + + /* + * and then wait for interrupt handler to mark completion. + */ + timeout_jiffies = msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT); + ret = wait_for_completion_timeout(&csi->last_eof_completion, + timeout_jiffies); + if (ret == 0) + v4l2_warn(&csi->sd, "wait last EOF timeout\n"); + + imx7_csi_hw_disable_irq(csi); + + imx7_csi_dma_unsetup_vb2_buf(csi, VB2_BUF_STATE_ERROR); + + imx_media_free_dma_buf(csi->imxmd, &csi->underrun_buf); +} + +static int imx7_csi_configure(struct imx7_csi *csi) +{ + struct imx_media_video_dev *vdev = csi->vdev; + struct v4l2_pix_format *out_pix = &vdev->fmt.fmt.pix; + __u32 in_code = csi->format_mbus[IMX7_CSI_PAD_SINK].code; + u32 cr1, cr18; + + if (out_pix->field == V4L2_FIELD_INTERLACED) { + imx7_csi_deinterlace_enable(csi, true); + imx7_csi_buf_stride_set(csi, out_pix->width); + } else { + imx7_csi_deinterlace_enable(csi, false); + imx7_csi_buf_stride_set(csi, 0); + } + + imx7_csi_set_imagpara(csi, out_pix->width, out_pix->height); + + if (!csi->is_csi2) + return 0; + + cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + cr1 &= ~BIT_GCLK_MODE; + + cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); + cr18 &= BIT_MIPI_DATA_FORMAT_MASK; + cr18 |= BIT_DATA_FROM_MIPI; + + switch (out_pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; + break; + case V4L2_PIX_FMT_SBGGR8: + cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; + break; + case V4L2_PIX_FMT_SBGGR16: + if (in_code == MEDIA_BUS_FMT_SBGGR10_1X10) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; + else if (in_code == MEDIA_BUS_FMT_SBGGR12_1X12) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; + else if (in_code == MEDIA_BUS_FMT_SBGGR14_1X14) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW14; + cr1 |= BIT_PIXEL_BIT; + break; + default: + return -EINVAL; + } + + imx7_csi_reg_write(csi, cr1, CSI_CSICR1); + imx7_csi_reg_write(csi, cr18, CSI_CSICR18); + + return 0; +} + +static int imx7_csi_enable(struct imx7_csi *csi) +{ + imx7_csi_sw_reset(csi); + + if (csi->is_csi2) { + imx7_csi_dmareq_rff_enable(csi); + imx7_csi_hw_enable_irq(csi); + imx7_csi_hw_enable(csi); + return 0; + } + + return 0; +} + +static void imx7_csi_disable(struct imx7_csi *csi) +{ + imx7_csi_dmareq_rff_disable(csi); + + imx7_csi_hw_disable_irq(csi); + + imx7_csi_buf_stride_set(csi, 0); + + imx7_csi_hw_disable(csi); +} + +static int imx7_csi_streaming_start(struct imx7_csi *csi) +{ + int ret; + + ret = imx7_csi_dma_start(csi); + if (ret < 0) + return ret; + + ret = imx7_csi_configure(csi); + if (ret < 0) + goto dma_stop; + + imx7_csi_enable(csi); + + return 0; + +dma_stop: + imx7_csi_dma_stop(csi); + + return ret; +} + +static int imx7_csi_streaming_stop(struct imx7_csi *csi) +{ + imx7_csi_dma_stop(csi); + + imx7_csi_disable(csi); + + return 0; +} + +static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&csi->lock); + + if (!csi->src_sd || !csi->sink) { + ret = -EPIPE; + goto out_unlock; + } + + if (csi->is_streaming == !!enable) + goto out_unlock; + + if (enable) { + ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1); + if (ret < 0) + goto out_unlock; + + ret = imx7_csi_streaming_start(csi); + if (ret < 0) { + v4l2_subdev_call(csi->src_sd, video, s_stream, 0); + goto out_unlock; + } + } else { + imx7_csi_streaming_stop(csi); + + v4l2_subdev_call(csi->src_sd, video, s_stream, 0); + } + + csi->is_streaming = !!enable; + +out_unlock: + mutex_unlock(&csi->lock); + + return ret; +} + +static struct v4l2_mbus_framefmt * +imx7_csi_get_format(struct imx7_csi *csi, + 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(&csi->sd, cfg, pad); + + return &csi->format_mbus[pad]; +} + +static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *in_fmt; + int ret = 0; + + mutex_lock(&csi->lock); + + in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK, code->which); + + switch (code->pad) { + case IMX7_CSI_PAD_SINK: + ret = imx_media_enum_mbus_format(&code->code, code->index, + CS_SEL_ANY, true); + break; + case IMX7_CSI_PAD_SRC: + if (code->index != 0) { + ret = -EINVAL; + goto out_unlock; + } + + code->code = in_fmt->code; + break; + default: + ret = -EINVAL; + } + +out_unlock: + mutex_unlock(&csi->lock); + + return ret; +} + +static int imx7_csi_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *fmt; + int ret = 0; + + mutex_lock(&csi->lock); + + fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which); + if (!fmt) { + ret = -EINVAL; + goto out_unlock; + } + + sdformat->format = *fmt; + +out_unlock: + mutex_unlock(&csi->lock); + + return ret; +} + +static int imx7_csi_try_fmt(struct imx7_csi *csi, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat, + const struct imx_media_pixfmt **cc) +{ + const struct imx_media_pixfmt *in_cc; + struct v4l2_mbus_framefmt *in_fmt; + u32 code; + + in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK, + sdformat->which); + if (!in_fmt) + return -EINVAL; + + switch (sdformat->pad) { + case IMX7_CSI_PAD_SRC: + in_cc = imx_media_find_mbus_format(in_fmt->code, CS_SEL_ANY, + true); + + sdformat->format.width = in_fmt->width; + sdformat->format.height = in_fmt->height; + sdformat->format.code = in_fmt->code; + *cc = in_cc; + + sdformat->format.colorspace = in_fmt->colorspace; + sdformat->format.xfer_func = in_fmt->xfer_func; + sdformat->format.quantization = in_fmt->quantization; + sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc; + break; + case IMX7_CSI_PAD_SINK: + *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, in_fmt, + false); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static int imx7_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct imx_media_video_dev *vdev = csi->vdev; + const struct imx_media_pixfmt *outcc; + struct v4l2_mbus_framefmt *outfmt; + struct v4l2_pix_format vdev_fmt; + struct v4l2_rect vdev_compose; + const struct imx_media_pixfmt *cc; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_format format; + int ret = 0; + + if (sdformat->pad >= IMX7_CSI_PADS_NUM) + return -EINVAL; + + mutex_lock(&csi->lock); + + if (csi->is_streaming) { + ret = -EBUSY; + goto out_unlock; + } + + imx7_csi_try_fmt(csi, cfg, sdformat, &cc); + + fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which); + if (!fmt) { + ret = -EINVAL; + goto out_unlock; + } + + *fmt = sdformat->format; + + if (sdformat->pad == IMX7_CSI_PAD_SINK) { + /* propagate format to source pads */ + format.pad = IMX7_CSI_PAD_SRC; + format.which = sdformat->which; + format.format = sdformat->format; + if (imx7_csi_try_fmt(csi, cfg, &format, &outcc)) { + ret = -EINVAL; + goto out_unlock; + } + outfmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SRC, + sdformat->which); + *outfmt = format.format; + + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + csi->cc[IMX7_CSI_PAD_SRC] = outcc; + } + + if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) + goto out_unlock; + + csi->cc[sdformat->pad] = cc; + + /* propagate output pad format to capture device */ + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, + &csi->format_mbus[IMX7_CSI_PAD_SRC], + csi->cc[IMX7_CSI_PAD_SRC]); + mutex_unlock(&csi->lock); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); + + return 0; + +out_unlock: + mutex_unlock(&csi->lock); + + return ret; +} + +static int imx7_csi_registered(struct v4l2_subdev *sd) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + int ret; + int i; + + for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { + csi->pad[i].flags = (i == IMX7_CSI_PAD_SINK) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + /* set a default mbus format */ + ret = imx_media_init_mbus_fmt(&csi->format_mbus[i], + 800, 600, 0, V4L2_FIELD_NONE, + &csi->cc[i]); + if (ret < 0) + return ret; + + /* init default frame interval */ + csi->frame_interval[i].numerator = 1; + csi->frame_interval[i].denominator = 30; + } + + ret = media_entity_pads_init(&sd->entity, IMX7_CSI_PADS_NUM, csi->pad); + if (ret < 0) + return ret; + + ret = imx_media_capture_device_register(csi->vdev); + if (ret < 0) + return ret; + + ret = imx_media_add_video_device(csi->imxmd, csi->vdev); + if (ret < 0) { + imx_media_capture_device_unregister(csi->vdev); + return ret; + } + + return 0; +} + +static void imx7_csi_unregistered(struct v4l2_subdev *sd) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + + imx_media_capture_device_unregister(csi->vdev); +} + +static int imx7_csi_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + int ret; + int i; + + for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { + mf = v4l2_subdev_get_try_format(sd, cfg, i); + + ret = imx_media_init_mbus_fmt(mf, 800, 600, 0, V4L2_FIELD_NONE, + &csi->cc[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct media_entity_operations imx7_csi_entity_ops = { + .link_setup = imx7_csi_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { + .s_stream = imx7_csi_s_stream, +}; + +static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { + .init_cfg = imx7_csi_init_cfg, + .enum_mbus_code = imx7_csi_enum_mbus_code, + .get_fmt = imx7_csi_get_fmt, + .set_fmt = imx7_csi_set_fmt, + .link_validate = imx7_csi_pad_link_validate, +}; + +static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { + .video = &imx7_csi_video_ops, + .pad = &imx7_csi_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { + .registered = imx7_csi_registered, + .unregistered = imx7_csi_unregistered, +}; + +static int imx7_csi_parse_endpoint(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) +{ + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; +} + +static int imx7_csi_clocks_get(struct imx7_csi *csi) +{ + struct device *dev = csi->dev; + int i; + + csi->num_clks = ARRAY_SIZE(imx7_csi_clk_id); + csi->clks = devm_kcalloc(dev, csi->num_clks, sizeof(*csi->clks), + GFP_KERNEL); + + if (!csi->clks) + return -ENOMEM; + + for (i = 0; i < csi->num_clks; i++) + csi->clks[i].id = imx7_csi_clk_id[i]; + + return devm_clk_bulk_get(dev, csi->num_clks, csi->clks); +} + +static int imx7_csi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct imx_media_dev *imxmd; + struct imx7_csi *csi; + struct resource *res; + int ret; + + csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + csi->dev = dev; + + ret = imx7_csi_clocks_get(csi); + if (ret < 0) { + dev_err(dev, "Failed to get clocks"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + csi->irq = platform_get_irq(pdev, 0); + if (!res || csi->irq < 0) { + dev_err(dev, "Missing platform resources data\n"); + return -ENODEV; + } + + csi->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(csi->regbase)) { + dev_err(dev, "Failed platform resources map\n"); + return -ENODEV; + } + + spin_lock_init(&csi->irqlock); + mutex_init(&csi->lock); + + /* install interrupt handler */ + ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi", + (void *)csi); + if (ret < 0) { + dev_err(dev, "Request CSI IRQ failed.\n"); + ret = -ENODEV; + goto destroy_mutex; + } + + /* add media device */ + imxmd = imx_media_dev_init(dev); + if (IS_ERR(imxmd)) { + ret = PTR_ERR(imxmd); + goto destroy_mutex; + } + platform_set_drvdata(pdev, &csi->sd); + + ret = imx_media_of_add_csi(imxmd, node); + if (ret < 0) + goto cleanup; + + ret = imx_media_dev_notifier_register(imxmd); + if (ret < 0) + goto cleanup; + + csi->imxmd = imxmd; + v4l2_subdev_init(&csi->sd, &imx7_csi_subdev_ops); + v4l2_set_subdevdata(&csi->sd, csi); + csi->sd.internal_ops = &imx7_csi_internal_ops; + csi->sd.entity.ops = &imx7_csi_entity_ops; + csi->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi->sd.dev = &pdev->dev; + csi->sd.owner = THIS_MODULE; + csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + csi->sd.grp_id = IMX_MEDIA_GRP_ID_CSI; + snprintf(csi->sd.name, sizeof(csi->sd.name), "csi"); + + csi->vdev = imx_media_capture_device_init(&csi->sd, IMX7_CSI_PAD_SRC); + if (IS_ERR(csi->vdev)) + return PTR_ERR(csi->vdev); + + v4l2_ctrl_handler_init(&csi->ctrl_hdlr, 0); + csi->sd.ctrl_handler = &csi->ctrl_hdlr; + + ret = v4l2_async_register_fwnode_subdev(&csi->sd, + sizeof(struct v4l2_async_subdev), + NULL, 0, + imx7_csi_parse_endpoint); + if (ret) + goto free; + + return 0; + +free: + imx_media_capture_device_unregister(csi->vdev); + imx_media_capture_device_remove(csi->vdev); + v4l2_ctrl_handler_free(&csi->ctrl_hdlr); + +cleanup: + v4l2_async_notifier_cleanup(&imxmd->notifier); + v4l2_device_unregister(&imxmd->v4l2_dev); + media_device_unregister(&imxmd->md); + media_device_cleanup(&imxmd->md); + +destroy_mutex: + mutex_destroy(&csi->lock); + + return ret; +} + +static int imx7_csi_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct imx_media_dev *imxmd = csi->imxmd; + + v4l2_async_notifier_unregister(&imxmd->notifier); + v4l2_async_notifier_cleanup(&imxmd->notifier); + + media_device_unregister(&imxmd->md); + v4l2_device_unregister(&imxmd->v4l2_dev); + media_device_cleanup(&imxmd->md); + + imx_media_capture_device_unregister(csi->vdev); + imx_media_capture_device_remove(csi->vdev); + + v4l2_async_unregister_subdev(sd); + v4l2_ctrl_handler_free(&csi->ctrl_hdlr); + + mutex_destroy(&csi->lock); + + return 0; +} + +static const struct of_device_id imx7_csi_of_match[] = { + { .compatible = "fsl,imx7-csi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, imx7_csi_of_match); + +static struct platform_driver imx7_csi_driver = { + .probe = imx7_csi_probe, + .remove = imx7_csi_remove, + .driver = { + .of_match_table = imx7_csi_of_match, + .name = "imx7-csi", + }, +}; +module_platform_driver(imx7_csi_driver); + +MODULE_DESCRIPTION("i.MX7 CSI subdev driver"); +MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx7-csi"); diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c new file mode 100644 index 000000000000..2ddcc42ab8ff --- /dev/null +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -0,0 +1,1160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver + * + * Copyright (C) 2019 Linaro Ltd + * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/regulator/consumer.h> +#include <linux/spinlock.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#include "imx-media.h" + +#define CSIS_DRIVER_NAME "imx7-mipi-csis" +#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME + +#define CSIS_PAD_SINK 0 +#define CSIS_PAD_SOURCE 1 +#define CSIS_PADS_NUM 2 + +#define MIPI_CSIS_DEF_PIX_WIDTH 640 +#define MIPI_CSIS_DEF_PIX_HEIGHT 480 + +/* Register map definition */ + +/* CSIS common control */ +#define MIPI_CSIS_CMN_CTRL 0x04 +#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW BIT(16) +#define MIPI_CSIS_CMN_CTRL_INTER_MODE BIT(10) +#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL BIT(2) +#define MIPI_CSIS_CMN_CTRL_RESET BIT(1) +#define MIPI_CSIS_CMN_CTRL_ENABLE BIT(0) + +#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8 +#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8) + +/* CSIS clock control */ +#define MIPI_CSIS_CLK_CTRL 0x08 +#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) ((x) << 28) +#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) ((x) << 24) +#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) ((x) << 20) +#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) ((x) << 16) +#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4) +#define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0) + +/* CSIS Interrupt mask */ +#define MIPI_CSIS_INTMSK 0x10 +#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31) +#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30) +#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29) +#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28) +#define MIPI_CSIS_INTMSK_FRAME_START BIT(24) +#define MIPI_CSIS_INTMSK_FRAME_END BIT(20) +#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16) +#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12) +#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8) +#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4) +#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3) +#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2) +#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1) +#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0) + +/* CSIS Interrupt source */ +#define MIPI_CSIS_INTSRC 0x14 +#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31) +#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30) +#define MIPI_CSIS_INTSRC_EVEN BIT(30) +#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29) +#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28) +#define MIPI_CSIS_INTSRC_ODD (0x3 << 28) +#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28) +#define MIPI_CSIS_INTSRC_FRAME_START BIT(24) +#define MIPI_CSIS_INTSRC_FRAME_END BIT(20) +#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16) +#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12) +#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8) +#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4) +#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3) +#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2) +#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1) +#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0) +#define MIPI_CSIS_INTSRC_ERRORS 0xfffff + +/* D-PHY status control */ +#define MIPI_CSIS_DPHYSTATUS 0x20 +#define MIPI_CSIS_DPHYSTATUS_ULPS_DAT BIT(8) +#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_DAT BIT(4) +#define MIPI_CSIS_DPHYSTATUS_ULPS_CLK BIT(1) +#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_CLK BIT(0) + +/* D-PHY common control */ +#define MIPI_CSIS_DPHYCTRL 0x24 +#define MIPI_CSIS_DPHYCTRL_HSS_MASK (0xff << 24) +#define MIPI_CSIS_DPHYCTRL_HSS_OFFSET 24 +#define MIPI_CSIS_DPHYCTRL_SCLKS_MASK (0x3 << 22) +#define MIPI_CSIS_DPHYCTRL_SCLKS_OFFSET 22 +#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_CLK BIT(6) +#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_DAT BIT(5) +#define MIPI_CSIS_DPHYCTRL_ENABLE_DAT BIT(1) +#define MIPI_CSIS_DPHYCTRL_ENABLE_CLK BIT(0) +#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0) + +/* D-PHY Master and Slave Control register Low */ +#define MIPI_CSIS_DPHYBCTRL_L 0x30 +/* D-PHY Master and Slave Control register High */ +#define MIPI_CSIS_DPHYBCTRL_H 0x34 +/* D-PHY Slave Control register Low */ +#define MIPI_CSIS_DPHYSCTRL_L 0x38 +/* D-PHY Slave Control register High */ +#define MIPI_CSIS_DPHYSCTRL_H 0x3c + +/* ISP Configuration register */ +#define MIPI_CSIS_ISPCONFIG_CH0 0x40 +#define MIPI_CSIS_ISPCONFIG_CH1 0x50 +#define MIPI_CSIS_ISPCONFIG_CH2 0x60 +#define MIPI_CSIS_ISPCONFIG_CH3 0x70 + +#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24) +#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) ((x) << 24) +#define MIPI_CSIS_ISPCFG_DOUBLE_CMPNT BIT(12) +#define MIPI_CSIS_ISPCFG_ALIGN_32BIT BIT(11) +#define MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT (0x1e << 2) +#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2) +#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2) +#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2) + +/* User defined formats, x = 1...4 */ +#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2) +#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2) + +/* ISP Image Resolution register */ +#define MIPI_CSIS_ISPRESOL_CH0 0x44 +#define MIPI_CSIS_ISPRESOL_CH1 0x54 +#define MIPI_CSIS_ISPRESOL_CH2 0x64 +#define MIPI_CSIS_ISPRESOL_CH3 0x74 +#define CSIS_MAX_PIX_WIDTH 0xffff +#define CSIS_MAX_PIX_HEIGHT 0xffff + +/* ISP SYNC register */ +#define MIPI_CSIS_ISPSYNC_CH0 0x48 +#define MIPI_CSIS_ISPSYNC_CH1 0x58 +#define MIPI_CSIS_ISPSYNC_CH2 0x68 +#define MIPI_CSIS_ISPSYNC_CH3 0x78 + +#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18 +#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12 +#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0 + +/* Non-image packet data buffers */ +#define MIPI_CSIS_PKTDATA_ODD 0x2000 +#define MIPI_CSIS_PKTDATA_EVEN 0x3000 +#define MIPI_CSIS_PKTDATA_SIZE SZ_4K + +#define DEFAULT_SCLK_CSIS_FREQ 166000000UL + +enum { + ST_POWERED = 1, + ST_STREAMING = 2, + ST_SUSPENDED = 4, +}; + +struct mipi_csis_event { + u32 mask; + const char * const name; + unsigned int counter; +}; + +static const struct mipi_csis_event mipi_csis_events[] = { + /* Errors */ + { MIPI_CSIS_INTSRC_ERR_SOT_HS, "SOT Error" }, + { MIPI_CSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" }, + { MIPI_CSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" }, + { MIPI_CSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" }, + { MIPI_CSIS_INTSRC_ERR_WRONG_CFG, "Wrong Configuration Error" }, + { MIPI_CSIS_INTSRC_ERR_ECC, "ECC Error" }, + { MIPI_CSIS_INTSRC_ERR_CRC, "CRC Error" }, + { MIPI_CSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" }, + /* Non-image data receive events */ + { MIPI_CSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" }, + { MIPI_CSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" }, + { MIPI_CSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" }, + { MIPI_CSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" }, + /* Frame start/end */ + { MIPI_CSIS_INTSRC_FRAME_START, "Frame Start" }, + { MIPI_CSIS_INTSRC_FRAME_END, "Frame End" }, +}; + +#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events) + +static const char * const mipi_csis_clk_id[] = {"pclk", "wrap", "phy"}; + +struct csis_hw_reset { + struct regmap *src; + u8 req_src; + u8 rst_bit; +}; + +struct csi_state { + /* lock elements below */ + struct mutex lock; + /* lock for event handler */ + spinlock_t slock; + struct device *dev; + struct media_pad pads[CSIS_PADS_NUM]; + struct v4l2_subdev mipi_sd; + struct v4l2_subdev *src_sd; + + u8 index; + struct platform_device *pdev; + struct phy *phy; + void __iomem *regs; + struct clk *wrap_clk; + int irq; + u32 flags; + + struct dentry *debugfs_root; + bool debug; + + int num_clks; + struct clk_bulk_data *clks; + + u32 clk_frequency; + u32 hs_settle; + + struct reset_control *mrst; + + const struct csis_pix_format *csis_fmt; + struct v4l2_mbus_framefmt format_mbus; + + struct v4l2_fwnode_bus_mipi_csi2 bus; + + struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS]; + + struct v4l2_async_notifier subdev_notifier; + + struct csis_hw_reset hw_reset; + struct regulator *mipi_phy_regulator; + bool sink_linked; +}; + +struct csis_pix_format { + unsigned int pix_width_alignment; + u32 code; + u32 fmt_reg; + u8 data_alignment; +}; + +static const struct csis_pix_format mipi_csis_formats[] = { + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, + .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .data_alignment = 8, + }, { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, + .data_alignment = 16, + } +}; + +#define mipi_csis_write(__csis, __r, __v) writel(__v, (__csis)->regs + (__r)) +#define mipi_csis_read(__csis, __r) readl((__csis)->regs + (__r)) + +static int mipi_csis_dump_regs(struct csi_state *state) +{ + struct device *dev = &state->pdev->dev; + unsigned int i; + u32 cfg; + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x04, "CTRL" }, + { 0x24, "DPHYCTRL" }, + { 0x08, "CLKCTRL" }, + { 0x20, "DPHYSTS" }, + { 0x10, "INTMSK" }, + { 0x40, "CONFIG_CH0" }, + { 0xC0, "DBG_CONFIG" }, + { 0x38, "DPHYSLAVE_L" }, + { 0x3C, "DPHYSLAVE_H" }, + }; + + dev_info(dev, "--- REGISTERS ---\n"); + + for (i = 0; i < ARRAY_SIZE(registers); i++) { + cfg = mipi_csis_read(state, registers[i].offset); + dev_info(dev, "%12s: 0x%08x\n", registers[i].name, cfg); + } + + return 0; +} + +static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev) +{ + return container_of(sdev, struct csi_state, mipi_sd); +} + +static const struct csis_pix_format *find_csis_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++) + if (code == mipi_csis_formats[i].code) + return &mipi_csis_formats[i]; + return NULL; +} + +static void mipi_csis_enable_interrupts(struct csi_state *state, bool on) +{ + mipi_csis_write(state, MIPI_CSIS_INTMSK, on ? 0xffffffff : 0); +} + +static void mipi_csis_sw_reset(struct csi_state *state) +{ + u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); + + mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, + val | MIPI_CSIS_CMN_CTRL_RESET); + usleep_range(10, 20); +} + +static int mipi_csis_phy_init(struct csi_state *state) +{ + state->mipi_phy_regulator = devm_regulator_get(state->dev, "phy"); + + return regulator_set_voltage(state->mipi_phy_regulator, 1000000, + 1000000); +} + +static void mipi_csis_phy_reset(struct csi_state *state) +{ + reset_control_assert(state->mrst); + + msleep(20); + + reset_control_deassert(state->mrst); +} + +static void mipi_csis_system_enable(struct csi_state *state, int on) +{ + u32 val, mask; + + val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); + if (on) + val |= MIPI_CSIS_CMN_CTRL_ENABLE; + else + val &= ~MIPI_CSIS_CMN_CTRL_ENABLE; + mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val); + + val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL); + val &= ~MIPI_CSIS_DPHYCTRL_ENABLE; + if (on) { + mask = (1 << (state->bus.num_data_lanes + 1)) - 1; + val |= (mask & MIPI_CSIS_DPHYCTRL_ENABLE); + } + mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val); +} + +/* Called with the state.lock mutex held */ +static void __mipi_csis_set_format(struct csi_state *state) +{ + struct v4l2_mbus_framefmt *mf = &state->format_mbus; + u32 val; + + /* Color format */ + val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0); + val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg; + mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val); + + /* Pixel resolution */ + val = mf->width | (mf->height << 16); + mipi_csis_write(state, MIPI_CSIS_ISPRESOL_CH0, val); +} + +static void mipi_csis_set_hsync_settle(struct csi_state *state, int hs_settle) +{ + u32 val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL); + + val = ((val & ~MIPI_CSIS_DPHYCTRL_HSS_MASK) | (hs_settle << 24)); + + mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val); +} + +static void mipi_csis_set_params(struct csi_state *state) +{ + int lanes = state->bus.num_data_lanes; + u32 val; + + val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); + val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK; + val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET; + mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val); + + __mipi_csis_set_format(state); + + mipi_csis_set_hsync_settle(state, state->hs_settle); + + val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0); + if (state->csis_fmt->data_alignment == 32) + val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT; + else + val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT; + mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val); + + val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) | + (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) | + (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET); + mipi_csis_write(state, MIPI_CSIS_ISPSYNC_CH0, val); + + val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL); + val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC; + if (state->wrap_clk) + val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC; + else + val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC; + + val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15); + val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK; + mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val); + + mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_L, 0x1f4); + mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_H, 0); + + /* Update the shadow register. */ + val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); + mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, + val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW | + MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL); +} + +static void mipi_csis_clk_enable(struct csi_state *state) +{ + int ret; + + ret = clk_bulk_prepare_enable(state->num_clks, state->clks); + if (ret < 0) + dev_err(state->dev, "failed to enable clocks\n"); +} + +static void mipi_csis_clk_disable(struct csi_state *state) +{ + clk_bulk_disable_unprepare(state->num_clks, state->clks); +} + +static int mipi_csis_clk_get(struct csi_state *state) +{ + struct device *dev = &state->pdev->dev; + unsigned int i; + int ret; + + state->num_clks = ARRAY_SIZE(mipi_csis_clk_id); + state->clks = devm_kcalloc(dev, state->num_clks, sizeof(*state->clks), + GFP_KERNEL); + + if (!state->clks) + return -ENOMEM; + + for (i = 0; i < state->num_clks; i++) + state->clks[i].id = mipi_csis_clk_id[i]; + + ret = devm_clk_bulk_get(dev, state->num_clks, state->clks); + if (ret < 0) + return ret; + + state->wrap_clk = devm_clk_get(dev, "wrap"); + if (IS_ERR(state->wrap_clk)) + return IS_ERR(state->wrap_clk); + + /* Set clock rate */ + ret = clk_set_rate(state->wrap_clk, state->clk_frequency); + if (ret < 0) + dev_err(dev, "set rate=%d failed: %d\n", state->clk_frequency, + ret); + + return ret; +} + +static void mipi_csis_start_stream(struct csi_state *state) +{ + mipi_csis_sw_reset(state); + mipi_csis_set_params(state); + mipi_csis_system_enable(state, true); + mipi_csis_enable_interrupts(state, true); +} + +static void mipi_csis_stop_stream(struct csi_state *state) +{ + mipi_csis_enable_interrupts(state, false); + mipi_csis_system_enable(state, false); +} + +static void mipi_csis_clear_counters(struct csi_state *state) +{ + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&state->slock, flags); + for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) + state->events[i].counter = 0; + spin_unlock_irqrestore(&state->slock, flags); +} + +static void mipi_csis_log_counters(struct csi_state *state, bool non_errors) +{ + int i = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 4; + struct device *dev = &state->pdev->dev; + unsigned long flags; + + spin_lock_irqsave(&state->slock, flags); + + for (i--; i >= 0; i--) { + if (state->events[i].counter > 0 || state->debug) + dev_info(dev, "%s events: %d\n", state->events[i].name, + state->events[i].counter); + } + spin_unlock_irqrestore(&state->slock, flags); +} + +/* + * V4L2 subdev operations + */ +static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + int ret = 0; + + if (enable) { + mipi_csis_clear_counters(state); + ret = pm_runtime_get_sync(&state->pdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&state->pdev->dev); + return ret; + } + ret = v4l2_subdev_call(state->src_sd, core, s_power, 1); + if (ret < 0) + return ret; + } + + mutex_lock(&state->lock); + if (enable) { + if (state->flags & ST_SUSPENDED) { + ret = -EBUSY; + goto unlock; + } + + mipi_csis_start_stream(state); + ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1); + if (ret < 0) + goto unlock; + + mipi_csis_log_counters(state, true); + + state->flags |= ST_STREAMING; + } else { + v4l2_subdev_call(state->src_sd, video, s_stream, 0); + ret = v4l2_subdev_call(state->src_sd, core, s_power, 1); + mipi_csis_stop_stream(state); + state->flags &= ~ST_STREAMING; + if (state->debug) + mipi_csis_log_counters(state, true); + } + +unlock: + mutex_unlock(&state->lock); + if (!enable) + pm_runtime_put(&state->pdev->dev); + + return ret; +} + +static int mipi_csis_link_setup(struct media_entity *entity, + const struct media_pad *local_pad, + const struct media_pad *remote_pad, u32 flags) +{ + struct v4l2_subdev *mipi_sd = media_entity_to_v4l2_subdev(entity); + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct v4l2_subdev *remote_sd; + int ret = 0; + + dev_dbg(state->dev, "link setup %s -> %s", remote_pad->entity->name, + local_pad->entity->name); + + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + mutex_lock(&state->lock); + + if (local_pad->flags & MEDIA_PAD_FL_SOURCE) { + if (flags & MEDIA_LNK_FL_ENABLED) { + if (state->sink_linked) { + ret = -EBUSY; + goto out; + } + state->sink_linked = true; + } else { + state->sink_linked = false; + } + } else { + if (flags & MEDIA_LNK_FL_ENABLED) { + if (state->src_sd) { + ret = -EBUSY; + goto out; + } + state->src_sd = remote_sd; + } else { + state->src_sd = NULL; + } + } + +out: + mutex_unlock(&state->lock); + return ret; +} + +static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_mbus_framefmt *mf; + unsigned int i; + int ret; + + for (i = 0; i < CSIS_PADS_NUM; i++) { + mf = v4l2_subdev_get_try_format(mipi_sd, cfg, i); + + ret = imx_media_init_mbus_fmt(mf, MIPI_CSIS_DEF_PIX_HEIGHT, + MIPI_CSIS_DEF_PIX_WIDTH, 0, + V4L2_FIELD_NONE, NULL); + if (ret < 0) + return ret; + } + + return 0; +} + +static struct csis_pix_format const * +mipi_csis_try_format(struct v4l2_subdev *mipi_sd, struct v4l2_mbus_framefmt *mf) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct csis_pix_format const *csis_fmt; + + csis_fmt = find_csis_format(mf->code); + if (!csis_fmt) + csis_fmt = &mipi_csis_formats[0]; + + v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, + csis_fmt->pix_width_alignment, + &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, + 0); + + state->format_mbus.code = csis_fmt->code; + state->format_mbus.width = mf->width; + state->format_mbus.height = mf->height; + + return csis_fmt; +} + +static struct v4l2_mbus_framefmt * +mipi_csis_get_format(struct csi_state *state, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which, + unsigned int pad) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&state->mipi_sd, cfg, pad); + + return &state->format_mbus; +} + +static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct csis_pix_format const *csis_fmt; + struct v4l2_mbus_framefmt *fmt; + + if (sdformat->pad >= CSIS_PADS_NUM) + return -EINVAL; + + fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + + mutex_lock(&state->lock); + if (fmt && sdformat->pad == CSIS_PAD_SOURCE) { + sdformat->format = *fmt; + goto unlock; + } + + csis_fmt = mipi_csis_try_format(mipi_sd, &sdformat->format); + + sdformat->format = *fmt; + + if (csis_fmt && sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + state->csis_fmt = csis_fmt; + else + cfg->try_fmt = sdformat->format; + +unlock: + mutex_unlock(&state->lock); + + return 0; +} + +static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + struct v4l2_mbus_framefmt *fmt; + + mutex_lock(&state->lock); + + fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad); + + sdformat->format = *fmt; + + mutex_unlock(&state->lock); + + return 0; +} + +static int mipi_csis_log_status(struct v4l2_subdev *mipi_sd) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + + mutex_lock(&state->lock); + mipi_csis_log_counters(state, true); + if (state->debug && (state->flags & ST_POWERED)) + mipi_csis_dump_regs(state); + mutex_unlock(&state->lock); + + return 0; +} + +static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id) +{ + struct csi_state *state = dev_id; + unsigned long flags; + unsigned int i; + u32 status; + + status = mipi_csis_read(state, MIPI_CSIS_INTSRC); + + spin_lock_irqsave(&state->slock, flags); + + /* Update the event/error counters */ + if ((status & MIPI_CSIS_INTSRC_ERRORS) || state->debug) { + for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) { + if (!(status & state->events[i].mask)) + continue; + state->events[i].counter++; + } + } + spin_unlock_irqrestore(&state->slock, flags); + + mipi_csis_write(state, MIPI_CSIS_INTSRC, status); + + return IRQ_HANDLED; +} + +static const struct v4l2_subdev_core_ops mipi_csis_core_ops = { + .log_status = mipi_csis_log_status, +}; + +static const struct media_entity_operations mipi_csis_entity_ops = { + .link_setup = mipi_csis_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_video_ops mipi_csis_video_ops = { + .s_stream = mipi_csis_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { + .init_cfg = mipi_csis_init_cfg, + .get_fmt = mipi_csis_get_fmt, + .set_fmt = mipi_csis_set_fmt, +}; + +static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { + .core = &mipi_csis_core_ops, + .video = &mipi_csis_video_ops, + .pad = &mipi_csis_pad_ops, +}; + +static int mipi_csis_parse_dt(struct platform_device *pdev, + struct csi_state *state) +{ + struct device_node *node = pdev->dev.of_node; + + if (of_property_read_u32(node, "clock-frequency", + &state->clk_frequency)) + state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; + + /* Get MIPI PHY resets */ + state->mrst = devm_reset_control_get_exclusive(&pdev->dev, "mrst"); + if (IS_ERR(state->mrst)) + return PTR_ERR(state->mrst); + + /* Get MIPI CSI-2 bus configuration from the endpoint node. */ + of_property_read_u32(node, "fsl,csis-hs-settle", &state->hs_settle); + + return 0; +} + +static int mipi_csis_pm_resume(struct device *dev, bool runtime); + +static int mipi_csis_parse_endpoint(struct device *dev, + struct v4l2_fwnode_endpoint *ep, + struct v4l2_async_subdev *asd) +{ + struct v4l2_subdev *mipi_sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + + if (ep->bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(dev, "invalid bus type, must be MIPI CSI2\n"); + return -EINVAL; + } + + state->bus = ep->bus.mipi_csi2; + + dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes); + dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); + + return 0; +} + +static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd, + struct platform_device *pdev, + const struct v4l2_subdev_ops *ops) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + unsigned int sink_port = 0; + int ret; + + v4l2_subdev_init(mipi_sd, ops); + mipi_sd->owner = THIS_MODULE; + snprintf(mipi_sd->name, sizeof(mipi_sd->name), "%s.%d", + CSIS_SUBDEV_NAME, state->index); + + mipi_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mipi_sd->ctrl_handler = NULL; + + mipi_sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + mipi_sd->entity.ops = &mipi_csis_entity_ops; + + mipi_sd->dev = &pdev->dev; + + state->csis_fmt = &mipi_csis_formats[0]; + state->format_mbus.code = mipi_csis_formats[0].code; + state->format_mbus.width = MIPI_CSIS_DEF_PIX_WIDTH; + state->format_mbus.height = MIPI_CSIS_DEF_PIX_HEIGHT; + state->format_mbus.field = V4L2_FIELD_NONE; + + v4l2_set_subdevdata(mipi_sd, &pdev->dev); + + ret = v4l2_async_register_fwnode_subdev(mipi_sd, + sizeof(struct v4l2_async_subdev), + &sink_port, 1, + mipi_csis_parse_endpoint); + if (ret < 0) + dev_err(&pdev->dev, "async fwnode register failed: %d\n", ret); + + return ret; +} + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> + +static int mipi_csis_dump_regs_show(struct seq_file *m, void *private) +{ + struct csi_state *state = m->private; + + return mipi_csis_dump_regs(state); +} +DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs); + +static int __init_or_module mipi_csis_debugfs_init(struct csi_state *state) +{ + struct dentry *d; + + if (!debugfs_initialized()) + return -ENODEV; + + state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL); + if (!state->debugfs_root) + return -ENOMEM; + + d = debugfs_create_bool("debug_enable", 0600, state->debugfs_root, + &state->debug); + if (!d) + goto remove_debugfs; + + d = debugfs_create_file("dump_regs", 0600, state->debugfs_root, + state, &mipi_csis_dump_regs_fops); + if (!d) + goto remove_debugfs; + + return 0; + +remove_debugfs: + debugfs_remove_recursive(state->debugfs_root); + + return -ENOMEM; +} + +static void mipi_csis_debugfs_exit(struct csi_state *state) +{ + debugfs_remove_recursive(state->debugfs_root); +} + +#else +static int mipi_csis_debugfs_init(struct csi_state *state __maybe_unused) +{ + return 0; +} + +static void mipi_csis_debugfs_exit(struct csi_state *state __maybe_unused) +{ +} +#endif + +static int mipi_csis_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *mem_res; + struct csi_state *state; + int ret = -ENOMEM; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->slock); + + state->pdev = pdev; + state->dev = dev; + + ret = mipi_csis_parse_dt(pdev, state); + if (ret < 0) { + dev_err(dev, "Failed to parse device tree: %d\n", ret); + return ret; + } + + mipi_csis_phy_init(state); + mipi_csis_phy_reset(state); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + state->regs = devm_ioremap_resource(dev, mem_res); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); + + state->irq = platform_get_irq(pdev, 0); + if (state->irq < 0) { + dev_err(dev, "Failed to get irq\n"); + return state->irq; + } + + ret = mipi_csis_clk_get(state); + if (ret < 0) + return ret; + + mipi_csis_clk_enable(state); + + ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler, + 0, dev_name(dev), state); + if (ret) { + dev_err(dev, "Interrupt request failed\n"); + goto disable_clock; + } + + platform_set_drvdata(pdev, &state->mipi_sd); + + mutex_init(&state->lock); + ret = mipi_csis_subdev_init(&state->mipi_sd, pdev, + &mipi_csis_subdev_ops); + if (ret < 0) + goto disable_clock; + + state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&state->mipi_sd.entity, CSIS_PADS_NUM, + state->pads); + if (ret < 0) + goto unregister_subdev; + + memcpy(state->events, mipi_csis_events, sizeof(state->events)); + + mipi_csis_debugfs_init(state); + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = mipi_csis_pm_resume(dev, true); + if (ret < 0) + goto unregister_all; + } + + dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", + state->bus.num_data_lanes, state->hs_settle, + state->wrap_clk ? 1 : 0, state->clk_frequency); + + return 0; + +unregister_all: + mipi_csis_debugfs_exit(state); + media_entity_cleanup(&state->mipi_sd.entity); +unregister_subdev: + v4l2_async_unregister_subdev(&state->mipi_sd); +disable_clock: + mipi_csis_clk_disable(state); + mutex_destroy(&state->lock); + + return ret; +} + +static int mipi_csis_pm_suspend(struct device *dev, bool runtime) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev); + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + int ret = 0; + + mutex_lock(&state->lock); + if (state->flags & ST_POWERED) { + mipi_csis_stop_stream(state); + ret = regulator_disable(state->mipi_phy_regulator); + if (ret) + goto unlock; + mipi_csis_clk_disable(state); + state->flags &= ~ST_POWERED; + if (!runtime) + state->flags |= ST_SUSPENDED; + } + +unlock: + mutex_unlock(&state->lock); + + return ret ? -EAGAIN : 0; +} + +static int mipi_csis_pm_resume(struct device *dev, bool runtime) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev); + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + int ret = 0; + + mutex_lock(&state->lock); + if (!runtime && !(state->flags & ST_SUSPENDED)) + goto unlock; + + if (!(state->flags & ST_POWERED)) { + ret = regulator_enable(state->mipi_phy_regulator); + if (ret) + goto unlock; + + state->flags |= ST_POWERED; + mipi_csis_clk_enable(state); + } + if (state->flags & ST_STREAMING) + mipi_csis_start_stream(state); + + state->flags &= ~ST_SUSPENDED; + +unlock: + mutex_unlock(&state->lock); + + return ret ? -EAGAIN : 0; +} + +static int __maybe_unused mipi_csis_suspend(struct device *dev) +{ + return mipi_csis_pm_suspend(dev, false); +} + +static int __maybe_unused mipi_csis_resume(struct device *dev) +{ + return mipi_csis_pm_resume(dev, false); +} + +static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev) +{ + return mipi_csis_pm_suspend(dev, true); +} + +static int __maybe_unused mipi_csis_runtime_resume(struct device *dev) +{ + return mipi_csis_pm_resume(dev, true); +} + +static int mipi_csis_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev); + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + + mipi_csis_debugfs_exit(state); + v4l2_async_unregister_subdev(&state->mipi_sd); + v4l2_async_notifier_unregister(&state->subdev_notifier); + + pm_runtime_disable(&pdev->dev); + mipi_csis_pm_suspend(&pdev->dev, true); + mipi_csis_clk_disable(state); + media_entity_cleanup(&state->mipi_sd.entity); + mutex_destroy(&state->lock); + pm_runtime_set_suspended(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops mipi_csis_pm_ops = { + SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume) +}; + +static const struct of_device_id mipi_csis_of_match[] = { + { .compatible = "fsl,imx7-mipi-csi2", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mipi_csis_of_match); + +static struct platform_driver mipi_csis_driver = { + .probe = mipi_csis_probe, + .remove = mipi_csis_remove, + .driver = { + .of_match_table = mipi_csis_of_match, + .name = CSIS_DRIVER_NAME, + .pm = &mipi_csis_pm_ops, + }, +}; + +module_platform_driver(mipi_csis_driver); + +MODULE_DESCRIPTION("i.MX7 MIPI CSI-2 Receiver driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx7-mipi-csi2"); diff --git a/drivers/staging/media/imx074/Kconfig b/drivers/staging/media/imx074/Kconfig deleted file mode 100644 index 229cbeea580b..000000000000 --- a/drivers/staging/media/imx074/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony diff --git a/drivers/staging/media/imx074/Makefile b/drivers/staging/media/imx074/Makefile deleted file mode 100644 index 7d183574aa84..000000000000 --- a/drivers/staging/media/imx074/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/staging/media/imx074/TODO b/drivers/staging/media/imx074/TODO deleted file mode 100644 index 15580a4f950c..000000000000 --- a/drivers/staging/media/imx074/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This sensor driver needs to be converted to a regular -v4l2 subdev driver. The soc_camera framework is deprecated and -will be removed in the future. Unless someone does this work this -sensor driver will be deleted when the soc_camera framework is -deleted. diff --git a/drivers/staging/media/ipu3/Makefile b/drivers/staging/media/ipu3/Makefile index fb146d178bd4..fa7fa3372bcb 100644 --- a/drivers/staging/media/ipu3/Makefile +++ b/drivers/staging/media/ipu3/Makefile @@ -9,3 +9,9 @@ ipu3-imgu-objs += \ ipu3-css.o ipu3-v4l2.o ipu3.o obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3-imgu.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, packed-not-aligned) +ccflags-y += $(call cc-disable-warning, type-limits) +ccflags-y += $(call cc-disable-warning, unused-const-variable) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index 905bbb190217..5e55baeaea1a 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -8,11 +8,6 @@ staging directory. - Using ENABLED and IMMUTABLE link flags for the links where those are relevant. (Sakari) -- Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to - imgu_v4l2_register(). (Sakari) - -- Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) - - IPU3 driver documentation (Laurent) Add diagram in driver rst to describe output capability. Comments on configuring v4l2 subdevs for CIO2 and ImgU. @@ -32,3 +27,5 @@ staging directory. - Document different operation modes, and which buffer queues are relevant in each mode. To process an image, which queues require a buffer an in which ones is it optional? + +- Make sure it builds fine with no warnings with W=1 diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h index ec0b74829351..1e7184e4311d 100644 --- a/drivers/staging/media/ipu3/include/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -16,12 +16,6 @@ #define V4L2_CID_INTEL_IPU3_BASE (V4L2_CID_USER_BASE + 0x10c0) #define V4L2_CID_INTEL_IPU3_MODE (V4L2_CID_INTEL_IPU3_BASE + 1) -/* custom ctrl to set pipe mode */ -enum ipu3_running_mode { - IPU3_RUNNING_MODE_VIDEO = 0, - IPU3_RUNNING_MODE_STILL = 1, -}; - /******************* ipu3_uapi_stats_3a *******************/ #define IPU3_UAPI_MAX_STRIPES 2 @@ -438,11 +432,11 @@ struct ipu3_uapi_awb_fr_raw_buffer { * * @grid_cfg: grid config, default 16x16. * @bayer_coeff: 1D Filter 1x11 center symmetry/anti-symmetry. - * coeffcients defaults { 0, 0, 0, 0, 0, 128 }. + * coefficients defaults { 0, 0, 0, 0, 0, 128 }. * Applied on whole image for each Bayer channel separately * by a weighted sum of its 11x1 neighbors. * @reserved1: reserved - * @bayer_sign: sign of filter coeffcients, default 0. + * @bayer_sign: sign of filter coefficients, default 0. * @bayer_nf: normalization factor for the convolution coeffs, to make sure * total memory needed is within pre-determined range. * NF should be the log2 of the sum of the abs values of the diff --git a/drivers/staging/media/ipu3/ipu3-abi.h b/drivers/staging/media/ipu3/ipu3-abi.h index 25be56ff01c8..e1185602c7fd 100644 --- a/drivers/staging/media/ipu3/ipu3-abi.h +++ b/drivers/staging/media/ipu3/ipu3-abi.h @@ -1510,7 +1510,7 @@ struct imgu_abi_blob_info { /* offset wrt hdr in bytes */ u32 prog_name_offset; /* offset wrt hdr in bytes */ u32 size; /* Size of blob */ - u32 padding_size; /* total cummulative of bytes added + u32 padding_size; /* total cumulative of bytes added * due to section alignment */ u32 icache_source; /* Position of icache in blob */ diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c index 55861aa8fb03..4122d4e42db6 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.c +++ b/drivers/staging/media/ipu3/ipu3-css-fw.c @@ -10,7 +10,7 @@ #include "ipu3-css-fw.h" #include "ipu3-dmamap.h" -static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, +static void imgu_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, const char *name) { unsigned int i; @@ -54,7 +54,7 @@ static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, dev_dbg(dev, "\n"); } -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi) { unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width, IMGU_OBGRID_TILE_SIZE * 2) + 1; @@ -69,7 +69,7 @@ unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) return obgrid_size; } -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, @@ -91,7 +91,7 @@ void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, return binary_params + par->offset; } -void ipu3_css_fw_cleanup(struct ipu3_css *css) +void imgu_css_fw_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -99,7 +99,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) unsigned int i; for (i = 0; i < css->fwp->file_header.binary_nr; i++) - ipu3_dmamap_free(imgu, &css->binary[i]); + imgu_dmamap_free(imgu, &css->binary[i]); kfree(css->binary); } if (css->fw) @@ -109,7 +109,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) css->fw = NULL; } -int ipu3_css_fw_init(struct ipu3_css *css) +int imgu_css_fw_init(struct imgu_css *css) { static const u32 BLOCK_MAX = 65536; struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -227,7 +227,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) css->fw->size) goto bad_fw; - ipu3_css_fw_show_binary(dev, bi, name); + imgu_css_fw_show_binary(dev, bi, name); } if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1) @@ -246,7 +246,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) void *blob = (void *)css->fwp + bi->blob.offset; size_t size = bi->blob.size; - if (!ipu3_dmamap_alloc(imgu, &css->binary[i], size)) { + if (!imgu_dmamap_alloc(imgu, &css->binary[i], size)) { r = -ENOMEM; goto error_out; } @@ -260,6 +260,6 @@ bad_fw: r = -ENODEV; error_out: - ipu3_css_fw_cleanup(css); + imgu_css_fw_cleanup(css); return r; } diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h index 07d8bb8b25f3..79ffa7045139 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.h +++ b/drivers/staging/media/ipu3/ipu3-css-fw.h @@ -175,11 +175,11 @@ struct imgu_fw_header { /******************* Firmware functions *******************/ -int ipu3_css_fw_init(struct ipu3_css *css); -void ipu3_css_fw_cleanup(struct ipu3_css *css); +int imgu_css_fw_init(struct imgu_css *css); +void imgu_css_fw_cleanup(struct imgu_css *css); -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi); -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi); +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 776206ded83b..4533dacad4be 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -6,6 +6,7 @@ #include "ipu3-css.h" #include "ipu3-css-fw.h" #include "ipu3-tables.h" +#include "ipu3-css-params.h" #define DIV_ROUND_CLOSEST_DOWN(a, b) (((a) + ((b) / 2) - 1) / (b)) #define roundclosest_down(a, b) (DIV_ROUND_CLOSEST_DOWN(a, b) * (b)) @@ -13,7 +14,7 @@ #define IPU3_UAPI_ANR_MAX_RESET ((1 << 12) - 1) #define IPU3_UAPI_ANR_MIN_RESET (((-1) << 12) + 1) -struct ipu3_css_scaler_info { +struct imgu_css_scaler_info { unsigned int phase_step; /* Same for luma/chroma */ int exp_shift; @@ -24,7 +25,7 @@ struct ipu3_css_scaler_info { int crop_top; }; -static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, +static unsigned int imgu_css_scaler_get_exp(unsigned int counter, unsigned int divider) { int i = fls(divider) - fls(counter); @@ -40,13 +41,13 @@ static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, /* Set up the CSS scaler look up table */ static void -ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, +imgu_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, unsigned int output_width, int phase_step_correction, const int *coeffs, unsigned int coeffs_size, - s8 coeff_lut[], struct ipu3_css_scaler_info *info) + s8 coeff_lut[], struct imgu_css_scaler_info *info) { int tap, phase, phase_sum_left, phase_sum_right; - int exponent = ipu3_css_scaler_get_exp(output_width, input_width); + int exponent = imgu_css_scaler_get_exp(output_width, input_width); int mantissa = (1 << exponent) * output_width; unsigned int phase_step; @@ -113,8 +114,8 @@ ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, * (must be perfectly aligned with hardware). */ static unsigned int -ipu3_css_scaler_calc_scaled_output(unsigned int input, - struct ipu3_css_scaler_info *info) +imgu_css_scaler_calc_scaled_output(unsigned int input, + struct imgu_css_scaler_info *info) { unsigned int arg1 = input * info->phase_step + (1 - IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES - @@ -132,10 +133,10 @@ ipu3_css_scaler_calc_scaled_output(unsigned int input, * and chroma details of a scaler */ static void -ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, +imgu_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, u32 target_height, struct imgu_abi_osys_config *cfg, - struct ipu3_css_scaler_info *info_luma, - struct ipu3_css_scaler_info *info_chroma, + struct imgu_css_scaler_info *info_luma, + struct imgu_css_scaler_info *info_chroma, unsigned int *output_width, unsigned int *output_height, unsigned int *procmode) { @@ -164,24 +165,24 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, do { phase_step_correction++; - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, input_width, target_width, phase_step_correction, - ipu3_css_downscale_4taps, + imgu_css_downscale_4taps, IMGU_SCALER_DOWNSCALE_4TAPS_LEN, cfg->scaler_coeffs_luma, info_luma); - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, input_width, target_width, phase_step_correction, - ipu3_css_downscale_2taps, + imgu_css_downscale_2taps, IMGU_SCALER_DOWNSCALE_2TAPS_LEN, cfg->scaler_coeffs_chroma, info_chroma); - out_width = ipu3_css_scaler_calc_scaled_output(input_width, + out_width = imgu_css_scaler_calc_scaled_output(input_width, info_luma); - out_height = ipu3_css_scaler_calc_scaled_output(input_height, + out_height = imgu_css_scaler_calc_scaled_output(input_height, info_luma); } while ((out_width < target_width || out_height < target_height || !IS_ALIGNED(out_height, height_alignment)) && @@ -193,7 +194,7 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, /********************** Osys routines for scaler****************************/ -static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, +static void imgu_css_osys_set_format(enum imgu_abi_frame_format host_format, unsigned int *osys_format, unsigned int *osys_tiling) { @@ -230,7 +231,7 @@ static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, * Function calculates input frame stripe offset, based * on output frame stripe offset and filter parameters. */ -static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_offset(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { @@ -244,12 +245,12 @@ static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, * Calculate input frame phase, given the output frame * stripe offset and filter parameters */ -static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_phase_init(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { int stripe_offset_inp = - ipu3_css_osys_calc_stripe_offset(stripe_offset_out, + imgu_css_osys_calc_stripe_offset(stripe_offset_out, fir_phases, phase_init, phase_step, pad_left); @@ -261,7 +262,7 @@ static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, * This function calculates input frame stripe width, * based on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, +static int imgu_css_osys_calc_inp_stripe_width(int stripe_width_out, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right) @@ -278,7 +279,7 @@ static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, * This function calculates output frame stripe width, basedi * on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, +static int imgu_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right, int column_offset) @@ -291,7 +292,7 @@ static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, return stripe_width_out - (fir_taps - 1); } -struct ipu3_css_reso { +struct imgu_css_reso { unsigned int input_width; unsigned int input_height; enum imgu_abi_frame_format input_format; @@ -305,7 +306,7 @@ struct ipu3_css_reso { int block_width; }; -struct ipu3_css_frame_params { +struct imgu_css_frame_params { /* Output pins */ unsigned int enable; unsigned int format; @@ -321,7 +322,7 @@ struct ipu3_css_frame_params { unsigned int crop_top; }; -struct ipu3_css_stripe_params { +struct imgu_css_stripe_params { unsigned int processing_mode; unsigned int phase_step; unsigned int exp_shift; @@ -358,20 +359,20 @@ struct ipu3_css_stripe_params { * frame_params - size IMGU_ABI_OSYS_PINS * stripe_params - size IPU3_UAPI_MAX_STRIPES */ -static int ipu3_css_osys_calc_frame_and_stripe_params( - struct ipu3_css *css, unsigned int stripes, +static int imgu_css_osys_calc_frame_and_stripe_params( + struct imgu_css *css, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, - struct ipu3_css_frame_params frame_params[], - struct ipu3_css_stripe_params stripe_params[], + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, + struct imgu_css_frame_params frame_params[], + struct imgu_css_stripe_params stripe_params[], unsigned int pipe) { - struct ipu3_css_reso reso; + struct imgu_css_reso reso; unsigned int output_width, pin, s; u32 input_width, input_height, target_width, target_height; unsigned int procmode = 0; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width; input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; @@ -463,7 +464,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( scaled = 1; } } - ipu3_css_osys_set_format(reso.pin_format[pin], &format, + imgu_css_osys_set_format(reso.pin_format[pin], &format, &tiling); } else { enable = 0; @@ -475,7 +476,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( frame_params[pin].scaled = scaled; } - ipu3_css_scaler_calc(input_width, input_height, target_width, + imgu_css_scaler_calc(input_width, input_height, target_width, target_height, osys, scaler_luma, scaler_chroma, &reso.pin_width[IMGU_ABI_OSYS_PIN_VF], &reso.pin_height[IMGU_ABI_OSYS_PIN_VF], &procmode); @@ -580,14 +581,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_offset_out_uv = stripe_offset_out_y / IMGU_LUMA_TO_CHROMA_RATIO; stripe_offset_inp_y = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_offset_inp_uv = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -596,14 +597,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( /* Calculate stripe phase init */ stripe_phase_init_y = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_phase_init_uv = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -707,7 +708,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( IMGU_LUMA_TO_CHROMA_RATIO; /* Calculate input stripe width */ stripe_input_width_y = stripe_offset_col_y + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -717,7 +718,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_pad_right_y); stripe_input_width_uv = stripe_offset_col_uv + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_uv, IMGU_OSYS_FIR_PHASES, stripe_phase_init_uv, @@ -752,7 +753,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( */ stripe_input_width_y = ALIGN(stripe_input_width_y, 8); stripe_output_width_y = - ipu3_css_osys_out_stripe_width( + imgu_css_osys_out_stripe_width( stripe_input_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -846,23 +847,23 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( * This function configures the Output Formatter System, given the number of * stripes, scaler luma and chrome parameters */ -static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_osys_calc(struct imgu_css *css, unsigned int pipe, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, struct imgu_abi_stripes block_stripes[]) { - struct ipu3_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; - struct ipu3_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; + struct imgu_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; + struct imgu_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; struct imgu_abi_osys_formatter_params *param; unsigned int pin, s; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; memset(osys, 0, sizeof(*osys)); /* Compute the frame and stripe params */ - if (ipu3_css_osys_calc_frame_and_stripe_params(css, stripes, osys, + if (imgu_css_osys_calc_frame_and_stripe_params(css, stripes, osys, scaler_luma, scaler_chroma, frame_params, @@ -1251,7 +1252,7 @@ static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, */ static int -ipu3_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, +imgu_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, const struct ipu3_uapi_shd_grid_config *grid, unsigned int image_height) { @@ -1495,7 +1496,7 @@ struct process_lines { /* Helper to config intra_frame_operations_data. */ static int -ipu3_css_acc_process_lines(const struct process_lines *pl, +imgu_css_acc_process_lines(const struct process_lines *pl, struct imgu_abi_acc_operation *p_op, struct imgu_abi_acc_process_lines_cmd_data *p_pl, struct imgu_abi_acc_transfer_op_data *p_tr) @@ -1632,12 +1633,12 @@ ipu3_css_acc_process_lines(const struct process_lines *pl, return 0; } -static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_af_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_af_config *af_config) { struct imgu_abi_af_intra_frame_operations_data *to = &af_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1655,17 +1656,17 @@ static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.af, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } static int -ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, +imgu_css_awb_fr_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_fr_config *awb_fr_config) { struct imgu_abi_awb_fr_intra_frame_operations_data *to = &awb_fr_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct process_lines pl = { @@ -1682,16 +1683,16 @@ ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_fr_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } -static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_awb_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_config *awb_config) { struct imgu_abi_awb_intra_frame_operations_data *to = &awb_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1708,33 +1709,33 @@ static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, to->transfer_data); } -static u16 ipu3_css_grid_end(u16 start, u8 width, u8 block_width_log2) +static u16 imgu_css_grid_end(u16 start, u8 width, u8 block_width_log2) { return (start & IPU3_UAPI_GRID_START_MASK) + (width << block_width_log2) - 1; } -static void ipu3_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) +static void imgu_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) { - grid_cfg->x_end = ipu3_css_grid_end(grid_cfg->x_start, grid_cfg->width, + grid_cfg->x_end = imgu_css_grid_end(grid_cfg->x_start, grid_cfg->width, grid_cfg->block_width_log2); - grid_cfg->y_end = ipu3_css_grid_end(grid_cfg->y_start, grid_cfg->height, + grid_cfg->y_end = imgu_css_grid_end(grid_cfg->y_start, grid_cfg->height, grid_cfg->block_height_log2); } /****************** config computation *****************************/ -static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; - struct ipu3_css_scaler_info scaler_luma, scaler_chroma; + struct imgu_css_scaler_info scaler_luma, scaler_chroma; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; const unsigned int f = IPU3_UAPI_ISP_VEC_ELEMS * 2; unsigned int bds_ds, i; @@ -1743,7 +1744,7 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, /* acc_param: osys_config */ - if (ipu3_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, + if (imgu_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, &scaler_chroma, acc->stripe.block_stripes)) return -EINVAL; @@ -1900,12 +1901,12 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, return 0; } -static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, +static void imgu_css_cfg_acc_dvs(struct imgu_css *css, struct imgu_abi_acc_param *acc, unsigned int pipe) { unsigned int i; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; /* Disable DVS statistics */ acc->dvs_stat.operations_data.process_lines_data[0].lines = @@ -1919,11 +1920,11 @@ static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, acc->dvs_stat.cfg.grd_config[i].enable = 0; } -static void acc_bds_per_stripe_data(struct ipu3_css *css, +static void acc_bds_per_stripe_data(struct imgu_css *css, struct imgu_abi_acc_param *acc, const int i, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_en = 0; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_start = 0; @@ -1944,13 +1945,13 @@ static void acc_bds_per_stripe_data(struct ipu3_css *css, * telling which fields to take from the old values (or generate if it is NULL) * and which to take from the new user values. */ -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -1959,7 +1960,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, const unsigned int min_overlap = 10; const struct v4l2_pix_format_mplane *pixm = &css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix; - const struct ipu3_css_bds_config *cfg_bds; + const struct imgu_css_bds_config *cfg_bds; struct imgu_abi_input_feeder_data *feeder_data; unsigned int bds_ds, ofs_x, ofs_y, i, width, height; @@ -1967,7 +1968,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, /* Update stripe using chroma and luma */ - if (ipu3_css_cfg_acc_stripe(css, pipe, acc)) + if (imgu_css_cfg_acc_stripe(css, pipe, acc)) return -EINVAL; /* acc_param: input_feeder_config */ @@ -2021,7 +2022,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->bnr = acc_old->bnr; } else { /* Calculate from scratch */ - acc->bnr = ipu3_css_bnr_defaults; + acc->bnr = imgu_css_bnr_defaults; } acc->bnr.column_size = tnr_frame_width; @@ -2049,7 +2050,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->dm = acc_old->dm; } else { /* Calculate from scratch */ - acc->dm = ipu3_css_dm_defaults; + acc->dm = imgu_css_dm_defaults; } acc->dm.frame_width = tnr_frame_width; @@ -2064,7 +2065,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->ccm = acc_old->ccm; } else { /* Calculate from scratch */ - acc->ccm = ipu3_css_ccm_defaults; + acc->ccm = imgu_css_ccm_defaults; } /* acc_param: gamma_config */ @@ -2078,7 +2079,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Calculate from scratch */ acc->gamma.gc_ctrl.enable = 1; - acc->gamma.gc_lut = ipu3_css_gamma_lut; + acc->gamma.gc_lut = imgu_css_gamma_lut; } /* acc_param: csc_mat_config */ @@ -2091,7 +2092,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->csc = acc_old->csc; } else { /* Calculate from scratch */ - acc->csc = ipu3_css_csc_defaults; + acc->csc = imgu_css_csc_defaults; } /* acc_param: cds_params */ @@ -2104,7 +2105,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->cds = acc_old->cds; } else { /* Calculate from scratch */ - acc->cds = ipu3_css_cds_defaults; + acc->cds = imgu_css_cds_defaults; } /* acc_param: shd_config */ @@ -2119,7 +2120,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd_lut = acc_old->shd.shd_lut; } else { /* Calculate from scratch */ - acc->shd.shd = ipu3_css_shd_defaults; + acc->shd.shd = imgu_css_shd_defaults; memset(&acc->shd.shd_lut, 0, sizeof(acc->shd.shd_lut)); } @@ -2137,12 +2138,12 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd.grid.block_height_log2) % acc->shd.shd.grid.grid_height_per_slice; - if (ipu3_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, + if (imgu_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, css_pipe->rect[IPU3_CSS_RECT_BDS].height)) return -EINVAL; /* acc_param: dvs_stat_config */ - ipu3_css_cfg_acc_dvs(css, acc, pipe); + imgu_css_cfg_acc_dvs(css, acc, pipe); /* acc_param: yuvp1_iefd_config */ @@ -2154,7 +2155,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->iefd = acc_old->iefd; } else { /* Calculate from scratch */ - acc->iefd = ipu3_css_iefd_defaults; + acc->iefd = imgu_css_iefd_defaults; } /* acc_param: yuvp1_yds_config yds_c0 */ @@ -2167,7 +2168,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds_c0 = acc_old->yds_c0; } else { /* Calculate from scratch */ - acc->yds_c0 = ipu3_css_yds_defaults; + acc->yds_c0 = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr_c0 */ @@ -2180,7 +2181,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr_c0 = acc_old->chnr_c0; } else { /* Calculate from scratch */ - acc->chnr_c0 = ipu3_css_chnr_defaults; + acc->chnr_c0 = imgu_css_chnr_defaults; } /* acc_param: yuvp1_y_ee_nr_config */ @@ -2193,7 +2194,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->y_ee_nr = acc_old->y_ee_nr; } else { /* Calculate from scratch */ - acc->y_ee_nr = ipu3_css_y_ee_nr_defaults; + acc->y_ee_nr = imgu_css_y_ee_nr_defaults; } /* acc_param: yuvp1_yds_config yds */ @@ -2206,7 +2207,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds = acc_old->yds; } else { /* Calculate from scratch */ - acc->yds = ipu3_css_yds_defaults; + acc->yds = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr */ @@ -2219,7 +2220,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr = acc_old->chnr; } else { /* Calculate from scratch */ - acc->chnr = ipu3_css_chnr_defaults; + acc->chnr = imgu_css_chnr_defaults; } /* acc_param: yuvp2_y_tm_lut_static_config */ @@ -2238,7 +2239,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds2 = acc_old->yds2; } else { /* Calculate from scratch */ - acc->yds2 = ipu3_css_yds_defaults; + acc->yds2 = imgu_css_yds_defaults; } /* acc_param: yuvp2_tcc_static_config */ @@ -2270,8 +2271,8 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, for (i = 7; i < IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS; i++) acc->tcc.inv_y_lut.entries[i] = 1024 >> (i - 6); - acc->tcc.gain_pcwl = ipu3_css_tcc_gain_pcwl_lut; - acc->tcc.r_sqr_lut = ipu3_css_tcc_r_sqr_lut; + acc->tcc.gain_pcwl = imgu_css_tcc_gain_pcwl_lut; + acc->tcc.r_sqr_lut = imgu_css_tcc_r_sqr_lut; } /* acc_param: dpc_config */ @@ -2287,10 +2288,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, bds_ds = (css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height * IMGU_BDS_GRANULARITY) / css_pipe->rect[IPU3_CSS_RECT_BDS].height; if (bds_ds < IMGU_BDS_MIN_SF_INV || - bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(ipu3_css_bds_configs)) + bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(imgu_css_bds_configs)) return -EINVAL; - cfg_bds = &ipu3_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; + cfg_bds = &imgu_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; acc->bds.hor.hor_ctrl1.hor_crop_en = 0; acc->bds.hor.hor_ctrl1.hor_crop_start = 0; acc->bds.hor.hor_ctrl1.hor_crop_end = 0; @@ -2339,7 +2340,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, sizeof(acc->anr.stitch.pyramid)); } else { /* Calculate from scratch */ - acc->anr = ipu3_css_anr_defaults; + acc->anr = imgu_css_anr_defaults; } /* Always enabled */ @@ -2377,10 +2378,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.config = acc_old->awb_fr.config; } else { /* Set from scratch */ - acc->awb_fr.config = ipu3_css_awb_fr_defaults; + acc->awb_fr.config = imgu_css_awb_fr_defaults; } - ipu3_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); + imgu_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); if (acc->awb_fr.config.grid_cfg.width <= 0) return -EINVAL; @@ -2415,7 +2416,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[0].grid_cfg.width; b_w_log2 = acc->awb_fr.stripes[0].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, acc->awb_fr.stripes[0].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[0].grid_cfg.x_end = end; @@ -2425,7 +2426,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->stripe.down_scaled_stripes[1].offset) & IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->awb_fr.stripes[1].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, acc->awb_fr.stripes[1].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[1].grid_cfg.x_end = end; @@ -2439,7 +2440,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) + if (imgu_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) return -EINVAL; /* acc_param: ae_config */ @@ -2461,18 +2462,18 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, static const struct ipu3_uapi_ae_weight_elem weight_def = { 1, 1, 1, 1, 1, 1, 1, 1 }; - acc->ae.grid_cfg = ipu3_css_ae_grid_defaults; - acc->ae.ae_ccm = ipu3_css_ae_ccm_defaults; + acc->ae.grid_cfg = imgu_css_ae_grid_defaults; + acc->ae.ae_ccm = imgu_css_ae_ccm_defaults; for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) acc->ae.weights[i] = weight_def; } b_w_log2 = acc->ae.grid_cfg.block_width_log2; - acc->ae.grid_cfg.x_end = ipu3_css_grid_end(acc->ae.grid_cfg.x_start, + acc->ae.grid_cfg.x_end = imgu_css_grid_end(acc->ae.grid_cfg.x_start, acc->ae.grid_cfg.width, b_w_log2); b_w_log2 = acc->ae.grid_cfg.block_height_log2; - acc->ae.grid_cfg.y_end = ipu3_css_grid_end(acc->ae.grid_cfg.y_start, + acc->ae.grid_cfg.y_end = imgu_css_grid_end(acc->ae.grid_cfg.y_start, acc->ae.grid_cfg.height, b_w_log2); @@ -2501,7 +2502,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->ae.stripes[0].grid.block_width_log2; acc->ae.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[0].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[0].grid.x_start, acc->ae.stripes[0].grid.width, b_w_log2); @@ -2511,7 +2512,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->ae.stripes[1].grid.block_width_log2; acc->ae.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[1].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[1].grid.x_start, acc->ae.stripes[1].grid.width, b_w_log2); } @@ -2528,11 +2529,11 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Set from scratch */ acc->af.config.filter_config = - ipu3_css_af_defaults.filter_config; - acc->af.config.grid_cfg = ipu3_css_af_defaults.grid_cfg; + imgu_css_af_defaults.filter_config; + acc->af.config.grid_cfg = imgu_css_af_defaults.grid_cfg; } - ipu3_css_grid_end_calc(&acc->af.config.grid_cfg); + imgu_css_grid_end_calc(&acc->af.config.grid_cfg); if (acc->af.config.grid_cfg.width <= 0) return -EINVAL; @@ -2578,7 +2579,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[0].grid_cfg.block_width_log2; acc->af.stripes[0].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, acc->af.stripes[0].grid_cfg.width, b_w_log2); @@ -2589,7 +2590,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[1].grid_cfg.block_width_log2; acc->af.stripes[1].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, acc->af.stripes[1].grid_cfg.width, b_w_log2); @@ -2601,7 +2602,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->af.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_af_ops_calc(css, pipe, &acc->af)) + if (imgu_css_af_ops_calc(css, pipe, &acc->af)) return -EINVAL; /* acc_param: awb_config */ @@ -2614,7 +2615,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config = acc_old->awb.config; } else { /* Set from scratch */ - acc->awb.config = ipu3_css_awb_defaults; + acc->awb.config = imgu_css_awb_defaults; } if (acc->awb.config.grid.width <= 0) @@ -2622,7 +2623,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config.grid.height_per_slice = IMGU_ABI_AWB_MAX_CELLS_PER_SET / acc->awb.config.grid.width, - ipu3_css_grid_end_calc(&acc->awb.config.grid); + imgu_css_grid_end_calc(&acc->awb.config.grid); for (i = 0; i < stripes; i++) acc->awb.stripes[i] = acc->awb.config; @@ -2647,7 +2648,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[0].grid.block_width_log2; acc->awb.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[0].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[0].grid.x_start, acc->awb.stripes[0].grid.width, b_w_log2); @@ -2658,7 +2659,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[1].grid.block_width_log2; acc->awb.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[1].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[1].grid.x_start, acc->awb.stripes[1].grid.width, b_w_log2); @@ -2670,7 +2671,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.stripes[i].grid.height_per_slice = 1; } - if (ipu3_css_awb_ops_calc(css, pipe, &acc->awb)) + if (imgu_css_awb_ops_calc(css, pipe, &acc->awb)) return -EINVAL; return 0; @@ -2685,7 +2686,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, * to the structure inside `new_binary_params'. In that case the caller * should calculate and fill the structure from scratch. */ -static void *ipu3_css_cfg_copy(struct ipu3_css *css, +static void *imgu_css_cfg_copy(struct imgu_css *css, unsigned int pipe, bool use_user, void *user_setting, void *old_binary_params, void *new_binary_params, @@ -2696,7 +2697,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; void *new_setting, *old_setting; - new_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + new_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, new_binary_params); if (!new_setting) return ERR_PTR(-EPROTO); /* Corrupted firmware */ @@ -2706,7 +2707,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, memcpy(new_setting, user_setting, par_size); } else if (old_binary_params) { /* Take previous value */ - old_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + old_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, old_binary_params); if (!old_setting) @@ -2722,7 +2723,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, /* * Configure VMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user) @@ -2744,7 +2745,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure Linearization VMEM0 parameters */ - lin_vmem = ipu3_css_cfg_copy(css, pipe, use && use->lin_vmem_params, + lin_vmem = imgu_css_cfg_copy(css, pipe, use && use->lin_vmem_params, &user->lin_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.lin, sizeof(*lin_vmem)); if (!IS_ERR_OR_NULL(lin_vmem)) { @@ -2764,7 +2765,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 VMEM parameters */ if (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_vmem = ipu3_css_cfg_copy(css, pipe, + tnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_vmem_params, &user->tnr3_vmem_params, vmem0_old, vmem0, m, @@ -2780,17 +2781,17 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 VMEM parameters */ - xnr_vmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, + xnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, &user->xnr3_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.xnr3, sizeof(*xnr_vmem)); if (!IS_ERR_OR_NULL(xnr_vmem)) { - xnr_vmem->x[i] = ipu3_css_xnr3_vmem_defaults.x + xnr_vmem->x[i] = imgu_css_xnr3_vmem_defaults.x [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->a[i] = ipu3_css_xnr3_vmem_defaults.a + xnr_vmem->a[i] = imgu_css_xnr3_vmem_defaults.a [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->b[i] = ipu3_css_xnr3_vmem_defaults.b + xnr_vmem->b[i] = imgu_css_xnr3_vmem_defaults.b [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->c[i] = ipu3_css_xnr3_vmem_defaults.c + xnr_vmem->c[i] = imgu_css_xnr3_vmem_defaults.c [i % IMGU_XNR3_VMEM_LUT_LEN]; } @@ -2801,12 +2802,12 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* * Configure DMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp + @@ -2824,7 +2825,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 DMEM0 parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_dmem = ipu3_css_cfg_copy(css, pipe, + tnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_dmem_params, &user->tnr3_dmem_params, dmem0_old, dmem0, m, @@ -2839,7 +2840,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 DMEM0 parameters */ - xnr_dmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, + xnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, &user->xnr3_dmem_params, dmem0_old, dmem0, m, &pofs->dmem.xnr3, sizeof(*xnr_dmem)); if (!IS_ERR_OR_NULL(xnr_dmem)) { @@ -2853,7 +2854,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, } /* Generate unity morphing table without morphing effect */ -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.h b/drivers/staging/media/ipu3/ipu3-css-params.h index f3a0a47117a4..ffaec6b7d5cc 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.h +++ b/drivers/staging/media/ipu3/ipu3-css-params.h @@ -4,23 +4,23 @@ #ifndef __IPU3_PARAMS_H #define __IPU3_PARAMS_H -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user); -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user); -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user); -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.c b/drivers/staging/media/ipu3/ipu3-css-pool.c index 6f271f81669b..fa5b7d3acef2 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.c +++ b/drivers/staging/media/ipu3/ipu3-css-pool.c @@ -7,30 +7,30 @@ #include "ipu3-css-pool.h" #include "ipu3-dmamap.h" -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size) +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size) { if (map->size < size && map->vaddr) { dev_warn(&imgu->pci_dev->dev, "dma buf resized from %zu to %zu", map->size, size); - ipu3_dmamap_free(imgu, map); - if (!ipu3_dmamap_alloc(imgu, map, size)) + imgu_dmamap_free(imgu, map); + if (!imgu_dmamap_alloc(imgu, map, size)) return -ENOMEM; } return 0; } -void ipu3_css_pool_cleanup(struct imgu_device *imgu, struct ipu3_css_pool *pool) +void imgu_css_pool_cleanup(struct imgu_device *imgu, struct imgu_css_pool *pool) { unsigned int i; for (i = 0; i < IPU3_CSS_POOL_SIZE; i++) - ipu3_dmamap_free(imgu, &pool->entry[i].param); + imgu_dmamap_free(imgu, &pool->entry[i].param); } -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size) { unsigned int i; @@ -42,7 +42,7 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, continue; } - if (!ipu3_dmamap_alloc(imgu, &pool->entry[i].param, size)) + if (!imgu_dmamap_alloc(imgu, &pool->entry[i].param, size)) goto fail; } @@ -51,14 +51,14 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, return 0; fail: - ipu3_css_pool_cleanup(imgu, pool); + imgu_css_pool_cleanup(imgu, pool); return -ENOMEM; } /* * Allocate a new parameter via recycling the oldest entry in the pool. */ -void ipu3_css_pool_get(struct ipu3_css_pool *pool) +void imgu_css_pool_get(struct imgu_css_pool *pool) { /* Get the oldest entry */ u32 n = (pool->last + 1) % IPU3_CSS_POOL_SIZE; @@ -70,25 +70,25 @@ void ipu3_css_pool_get(struct ipu3_css_pool *pool) /* * Undo, for all practical purposes, the effect of pool_get(). */ -void ipu3_css_pool_put(struct ipu3_css_pool *pool) +void imgu_css_pool_put(struct imgu_css_pool *pool) { pool->entry[pool->last].valid = false; pool->last = (pool->last + IPU3_CSS_POOL_SIZE - 1) % IPU3_CSS_POOL_SIZE; } /** - * ipu3_css_pool_last - Retrieve the nth pool entry from last + * imgu_css_pool_last - Retrieve the nth pool entry from last * - * @pool: a pointer to &struct ipu3_css_pool. + * @pool: a pointer to &struct imgu_css_pool. * @n: the distance to the last index. * * Returns: * The nth entry from last or null map to indicate no frame stored. */ -const struct ipu3_css_map * -ipu3_css_pool_last(struct ipu3_css_pool *pool, unsigned int n) +const struct imgu_css_map * +imgu_css_pool_last(struct imgu_css_pool *pool, unsigned int n) { - static const struct ipu3_css_map null_map = { 0 }; + static const struct imgu_css_map null_map = { 0 }; int i = (pool->last + IPU3_CSS_POOL_SIZE - n) % IPU3_CSS_POOL_SIZE; WARN_ON(n >= IPU3_CSS_POOL_SIZE); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.h b/drivers/staging/media/ipu3/ipu3-css-pool.h index 2657c39a4d71..f4a60b41401b 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.h +++ b/drivers/staging/media/ipu3/ipu3-css-pool.h @@ -10,15 +10,15 @@ struct imgu_device; #define IPU3_CSS_POOL_SIZE 4 /** - * ipu3_css_map - store DMA mapping info for buffer + * imgu_css_map - store DMA mapping info for buffer * * @size: size of the buffer in bytes. * @vaddr: kernel virtual address. * @daddr: iova dma address to access IPU3. * @vma: private, a pointer to &struct vm_struct, - * used for ipu3_dmamap_free. + * used for imgu_dmamap_free. */ -struct ipu3_css_map { +struct imgu_css_map { size_t size; void *vaddr; dma_addr_t daddr; @@ -26,30 +26,30 @@ struct ipu3_css_map { }; /** - * ipu3_css_pool - circular buffer pool definition + * imgu_css_pool - circular buffer pool definition * * @entry: array with IPU3_CSS_POOL_SIZE elements. - * @entry.param: a &struct ipu3_css_map for storing the mem mapping. + * @entry.param: a &struct imgu_css_map for storing the mem mapping. * @entry.valid: used to mark if the entry has valid data. * @last: write pointer, initialized to IPU3_CSS_POOL_SIZE. */ -struct ipu3_css_pool { +struct imgu_css_pool { struct { - struct ipu3_css_map param; + struct imgu_css_map param; bool valid; } entry[IPU3_CSS_POOL_SIZE]; u32 last; }; -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size); -void ipu3_css_pool_cleanup(struct imgu_device *imgu, - struct ipu3_css_pool *pool); -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size); +void imgu_css_pool_cleanup(struct imgu_device *imgu, + struct imgu_css_pool *pool); +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size); -void ipu3_css_pool_get(struct ipu3_css_pool *pool); -void ipu3_css_pool_put(struct ipu3_css_pool *pool); -const struct ipu3_css_map *ipu3_css_pool_last(struct ipu3_css_pool *pool, +void imgu_css_pool_get(struct imgu_css_pool *pool); +void imgu_css_pool_put(struct imgu_css_pool *pool); +const struct imgu_css_map *imgu_css_pool_last(struct imgu_css_pool *pool, u32 last); #endif diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index 44c55639389a..15ab77e4b766 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -46,7 +46,7 @@ IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF) /* Formats supported by IPU3 Camera Sub System */ -static const struct ipu3_css_format ipu3_css_formats[] = { +static const struct imgu_css_format imgu_css_formats[] = { { .pixelformat = V4L2_PIX_FMT_NV12, .colorspace = V4L2_COLORSPACE_SRGB, @@ -100,7 +100,7 @@ static const struct ipu3_css_format ipu3_css_formats[] = { static const struct { enum imgu_abi_queue_id qid; size_t ptr_ofs; -} ipu3_css_queues[IPU3_CSS_QUEUES] = { +} imgu_css_queues[IPU3_CSS_QUEUES] = { [IPU3_CSS_QUEUE_IN] = { IMGU_ABI_QUEUE_C_ID, offsetof(struct imgu_abi_buffer, payload.frame.frame_data) @@ -120,7 +120,7 @@ static const struct { }; /* Initialize queue based on given format, adjust format as needed */ -static int ipu3_css_queue_init(struct ipu3_css_queue *queue, +static int imgu_css_queue_init(struct imgu_css_queue *queue, struct v4l2_pix_format_mplane *fmt, u32 flags) { struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix; @@ -133,11 +133,11 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, if (!fmt) return 0; - for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) { - if (!(ipu3_css_formats[i].flags & flags)) + for (i = 0; i < ARRAY_SIZE(imgu_css_formats); i++) { + if (!(imgu_css_formats[i].flags & flags)) continue; - queue->css_fmt = &ipu3_css_formats[i]; - if (ipu3_css_formats[i].pixelformat == fmt->pixelformat) + queue->css_fmt = &imgu_css_formats[i]; + if (imgu_css_formats[i].pixelformat == fmt->pixelformat) break; } if (!queue->css_fmt) @@ -178,7 +178,7 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, return 0; } -static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q) +static bool imgu_css_queue_enabled(struct imgu_css_queue *q) { return q->css_fmt; } @@ -200,7 +200,7 @@ static inline void writes(const void *mem, ssize_t count, void __iomem *addr) } /* Wait until register `reg', masked with `mask', becomes `cmp' */ -static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) +static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) { u32 val; @@ -210,7 +210,7 @@ static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) /* Initialize the IPU3 CSS hardware and associated h/w blocks */ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base) +int imgu_css_set_powerup(struct device *dev, void __iomem *base) { static const unsigned int freq = 450; u32 pm_ctrl, state, val; @@ -221,7 +221,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) { dev_err(dev, "failed to set CSS idle\n"); goto fail; @@ -245,7 +245,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) if (state & IMGU_STATE_POWER_DOWN) { writel(IMGU_PM_CTRL_RACE_TO_HALT | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to power up CSS\n"); goto fail; @@ -263,7 +263,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) val = pm_ctrl & ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); writel(val, base + IMGU_REG_PM_CTRL); writel(0, base + IMGU_REG_GP_BUSY); - if (ipu3_hw_wait(base, IMGU_REG_STATE, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_PWRDNM_FSM_MASK, 0)) { dev_err(dev, "failed to pwrdn CSS\n"); goto fail; @@ -273,7 +273,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(1, base + IMGU_REG_GP_BUSY); writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_HALT, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) { dev_err(dev, "failed to halt CSS\n"); goto fail; @@ -281,7 +281,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to start CSS\n"); goto fail; } @@ -296,26 +296,26 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) return 0; fail: - ipu3_css_set_powerdown(dev, base); + imgu_css_set_powerdown(dev, base); return -EIO; } -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) +void imgu_css_set_powerdown(struct device *dev, void __iomem *base) { dev_dbg(dev, "%s\n", __func__); /* wait for cio idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, + if (imgu_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, IMGU_CIO_GATE_BURST_MASK, 0)) dev_warn(dev, "wait cio gate idle timeout"); /* wait for css idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_warn(dev, "wait css idle timeout\n"); /* do halt-halted handshake with css */ writel(1, base + IMGU_REG_GP_HALT); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) dev_warn(dev, "failed to halt css"); @@ -323,7 +323,7 @@ void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); } -static void ipu3_css_hw_enable_irq(struct ipu3_css *css) +static void imgu_css_hw_enable_irq(struct imgu_css *css) { void __iomem *const base = css->base; u32 val, i; @@ -371,7 +371,7 @@ static void ipu3_css_hw_enable_irq(struct ipu3_css *css) } } -static int ipu3_css_hw_init(struct ipu3_css *css) +static int imgu_css_hw_init(struct imgu_css *css) { /* For checking that streaming monitor statuses are valid */ static const struct { @@ -463,11 +463,11 @@ static int ipu3_css_hw_init(struct ipu3_css *css) /* Initialize GDC with default values */ - for (i = 0; i < ARRAY_SIZE(ipu3_css_gdc_lut[0]); i++) { - u32 val0 = ipu3_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; - u32 val1 = ipu3_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; - u32 val2 = ipu3_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; - u32 val3 = ipu3_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; + for (i = 0; i < ARRAY_SIZE(imgu_css_gdc_lut[0]); i++) { + u32 val0 = imgu_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; + u32 val1 = imgu_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; + u32 val2 = imgu_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; + u32 val3 = imgu_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; writel(val0 | (val1 << 16), base + IMGU_REG_GDC_LUT_BASE + i * 8); @@ -479,7 +479,7 @@ static int ipu3_css_hw_init(struct ipu3_css *css) } /* Boot the given IPU3 CSS SP */ -static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) +static int imgu_css_hw_start_sp(struct imgu_css *css, int sp) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; @@ -501,7 +501,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) writel(readl(base + IMGU_REG_SP_CTRL(sp)) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_SP_CTRL(sp)); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + if (imgu_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + bi->info.sp.sw_state, ~0, IMGU_ABI_SP_SWSTATE_INITIALIZED)) return -EIO; @@ -510,7 +510,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) } /* Start the IPU3 CSS ImgU (Imaging Unit) and all the SPs */ -static int ipu3_css_hw_start(struct ipu3_css *css) +static int imgu_css_hw_start(struct imgu_css *css) { static const u32 event_mask = ((1 << IMGU_ABI_EVTTYPE_OUT_FRAME_DONE) | @@ -560,7 +560,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(readl(base + IMGU_REG_ISP_CTRL) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_ISP_CTRL); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + if (imgu_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + bl->info.bl.sw_state, ~0, IMGU_ABI_BL_SWSTATE_OK)) { dev_err(css->dev, "failed to start bootloader\n"); @@ -581,7 +581,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state); writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb); - if (ipu3_css_hw_start_sp(css, 0)) + if (imgu_css_hw_start_sp(css, 0)) return -EIO; writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.isp_started); @@ -608,7 +608,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(IMGU_ABI_SP_SWSTATE_TERMINATED, base + IMGU_REG_SP_DMEM_BASE(1) + bi->info.sp.sw_state); - if (ipu3_css_hw_start_sp(css, 1)) + if (imgu_css_hw_start_sp(css, 1)) return -EIO; writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(1) @@ -617,7 +617,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) return 0; } -static void ipu3_css_hw_stop(struct ipu3_css *css) +static void imgu_css_hw_stop(struct imgu_css *css) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; @@ -626,18 +626,18 @@ static void ipu3_css_hw_stop(struct ipu3_css *css) writel(IMGU_ABI_SP_COMM_COMMAND_TERMINATE, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_CTRL(0), + if (imgu_hw_wait(css->base, IMGU_REG_SP_CTRL(0), IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait sp0 idle timeout.\n"); if (readl(base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state) != IMGU_ABI_SP_SWSTATE_TERMINATED) dev_err(css->dev, "sp0 is not terminated.\n"); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_CTRL, + if (imgu_hw_wait(css->base, IMGU_REG_ISP_CTRL, IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait isp idle timeout\n"); } -static void ipu3_css_hw_cleanup(struct ipu3_css *css) +static void imgu_css_hw_cleanup(struct imgu_css *css) { void __iomem *const base = css->base; @@ -648,7 +648,7 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_err(css->dev, "failed to shut down hw cleanly\n"); @@ -659,19 +659,19 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) usleep_range(200, 300); } -static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipeline_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i; - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.parameter_set_info); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.binary_params_p[i]); } @@ -679,7 +679,7 @@ static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) * This function initializes various stages of the * IPU3 CSS ISP pipeline */ -static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_pipeline_init(struct imgu_css *css, unsigned int pipe) { static const int BYPC = 2; /* Bytes per component */ static const struct imgu_abi_buffer_sp buffer_sp_init = { @@ -697,7 +697,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) const int stage = 0; unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -725,7 +725,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure iterator */ - cfg_iter = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_iter = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.iterator, sizeof(*cfg_iter), vaddr); if (!cfg_iter) @@ -791,7 +791,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure reference (delay) frames */ - cfg_ref = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.ref, sizeof(*cfg_ref), vaddr); if (!cfg_ref) @@ -821,7 +821,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure DVS (digital video stabilization) */ - cfg_dvs = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_dvs = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.dvs, sizeof(*cfg_dvs), vaddr); if (!cfg_dvs) @@ -837,7 +837,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure TNR (temporal noise reduction) */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - cfg_tnr = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_tnr = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.tnr3, sizeof(*cfg_tnr), vaddr); @@ -868,7 +868,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) cfg = IMGU_ABI_PARAM_CLASS_STATE; vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr; - cfg_ref_state = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref_state = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.ref, sizeof(*cfg_ref_state), vaddr); @@ -881,7 +881,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure tnr dmem state parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { cfg_tnr_state = - ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.tnr3, sizeof(*cfg_tnr_state), vaddr); @@ -1068,21 +1068,21 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Initialize parameter pools */ - if (ipu3_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, + if (imgu_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, sizeof(struct imgu_abi_parameter_set_info)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.acc, + imgu_css_pool_init(imgu, &css_pipe->pool.acc, sizeof(struct imgu_abi_acc_param)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.gdc, + imgu_css_pool_init(imgu, &css_pipe->pool.gdc, sizeof(struct imgu_abi_gdc_warp_param) * 3 * cfg_dvs->num_horizontal_blocks / 2 * cfg_dvs->num_vertical_blocks) || - ipu3_css_pool_init(imgu, &css_pipe->pool.obgrid, - ipu3_css_fw_obgrid_size( + imgu_css_pool_init(imgu, &css_pipe->pool.obgrid, + imgu_css_fw_obgrid_size( &css->fwp->binary_header[css_pipe->bindex]))) goto out_of_memory; for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (ipu3_css_pool_init(imgu, + if (imgu_css_pool_init(imgu, &css_pipe->pool.binary_params_p[i], bi->info.isp.sp.mem_initializers.params [IMGU_ABI_PARAM_CLASS_PARAM][i].size)) @@ -1091,15 +1091,15 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) return 0; bad_firmware: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -EPROTO; out_of_memory: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -ENOMEM; } -static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) +static u8 imgu_css_queue_pos(struct imgu_css *css, int queue, int thread) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1112,7 +1112,7 @@ static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) } /* Sent data to sp using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_queue_data(struct ipu3_css *css, +static int imgu_css_queue_data(struct imgu_css *css, int queue, int thread, u32 data) { static const unsigned int sp; @@ -1151,7 +1151,7 @@ static int ipu3_css_queue_data(struct ipu3_css *css, } /* Receive data using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) +static int imgu_css_dequeue_data(struct imgu_css *css, int queue, u32 *data) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1188,7 +1188,7 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) writeb(start2, &q->sp2host_evtq_info.start); /* Acknowledge events dequeued from event queue */ - r = ipu3_css_queue_data(css, queue, 0, + r = imgu_css_queue_data(css, queue, 0, IMGU_ABI_EVENT_EVENT_DEQUEUED); if (r < 0) return r; @@ -1198,52 +1198,52 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) } /* Free binary-specific resources */ -static void ipu3_css_binary_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_binary_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->binary_params_cs[j][i]); j = IPU3_CSS_AUX_FRAME_REF; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); j = IPU3_CSS_AUX_FRAME_TNR; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); } -static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_preallocate(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->binary_params_cs[j - 1][i], CSS_ABI_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF]. mem[i], CSS_BDS_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR]. mem[i], CSS_GDC_SIZE)) goto out_of_memory; @@ -1251,14 +1251,14 @@ static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } /* allocate binary-specific resources */ -static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_setup(struct imgu_css *css, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_device *imgu = dev_get_drvdata(css->dev); int i, j, size; @@ -1269,7 +1269,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) { - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->binary_params_cs[j - 1][i], bi->info.isp.sp.mem_initializers.params[j][i].size)) @@ -1292,7 +1292,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w; size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i], size)) @@ -1313,7 +1313,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height; size = w * ALIGN(h * 3 / 2 + 3, 2); /* +3 for vf_pp prefetch */ for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], size)) @@ -1322,11 +1322,11 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } -int ipu3_css_start_streaming(struct ipu3_css *css) +int imgu_css_start_streaming(struct imgu_css *css) { u32 data; int r, pipe; @@ -1335,48 +1335,48 @@ int ipu3_css_start_streaming(struct ipu3_css *css) return -EPROTO; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_binary_setup(css, pipe); + r = imgu_css_binary_setup(css, pipe); if (r < 0) return r; } - r = ipu3_css_hw_init(css); + r = imgu_css_hw_init(css); if (r < 0) return r; - r = ipu3_css_hw_start(css); + r = imgu_css_hw_start(css); if (r < 0) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_pipeline_init(css, pipe); + r = imgu_css_pipeline_init(css, pipe); if (r < 0) goto fail; } css->streaming = true; - ipu3_css_hw_enable_irq(css); + imgu_css_hw_enable_irq(css); /* Initialize parameters to default */ for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_set_parameters(css, pipe, NULL); + r = imgu_css_set_parameters(css, pipe, NULL); if (r < 0) goto fail; } - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) ; if (r != -EBUSY) goto fail; - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) ; if (r != -EBUSY) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_START_STREAM | pipe << 16); if (r < 0) @@ -1387,22 +1387,22 @@ int ipu3_css_start_streaming(struct ipu3_css *css) fail: css->streaming = false; - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - ipu3_css_pipeline_cleanup(css, pipe); - ipu3_css_binary_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); } return r; } -void ipu3_css_stop_streaming(struct ipu3_css *css) +void imgu_css_stop_streaming(struct imgu_css *css) { - struct ipu3_css_buffer *b, *b0; + struct imgu_css_buffer *b, *b0; int q, r, pipe; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_STOP_STREAM); if (r < 0) dev_warn(css->dev, "failed on stop stream event\n"); @@ -1411,14 +1411,14 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) if (!css->streaming) return; - ipu3_css_hw_stop(css); + imgu_css_hw_stop(css); - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1434,10 +1434,10 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) css->streaming = false; } -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe) { int q; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1447,44 +1447,44 @@ bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) return (q == IPU3_CSS_QUEUES); } -bool ipu3_css_queue_empty(struct ipu3_css *css) +bool imgu_css_queue_empty(struct imgu_css *css) { unsigned int pipe; bool ret = 0; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ret &= ipu3_css_pipe_queue_empty(css, pipe); + ret &= imgu_css_pipe_queue_empty(css, pipe); return ret; } -bool ipu3_css_is_streaming(struct ipu3_css *css) +bool imgu_css_is_streaming(struct imgu_css *css) { return css->streaming; } -static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_map_init(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i; /* Allocate and map common structures with imgu hardware */ for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_sp_stage_ptrs[p][i], sizeof(struct imgu_abi_sp_stage))) return -ENOMEM; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_isp_stage_ptrs[p][i], sizeof(struct imgu_abi_isp_stage))) return -ENOMEM; } - if (!ipu3_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, + if (!imgu_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, ALIGN(sizeof(struct imgu_abi_ddr_address_map), IMGU_ABI_ISP_DDR_WORD_BYTES))) return -ENOMEM; @@ -1493,58 +1493,58 @@ static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) unsigned int abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->abi_buffers[q][i], sizeof(struct imgu_abi_buffer))) return -ENOMEM; } - if (ipu3_css_binary_preallocate(css, pipe)) { - ipu3_css_binary_cleanup(css, pipe); + if (imgu_css_binary_preallocate(css, pipe)) { + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } return 0; } -static void ipu3_css_pipe_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipe_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i, abi_buf_num; - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); for (q = 0; q < IPU3_CSS_QUEUES; q++) { abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - ipu3_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); + imgu_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); } for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_sp_stage_ptrs[p][i]); - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_isp_stage_ptrs[p][i]); } - ipu3_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); + imgu_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); } -void ipu3_css_cleanup(struct ipu3_css *css) +void imgu_css_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int pipe; - ipu3_css_stop_streaming(css); + imgu_css_stop_streaming(css); for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ipu3_css_pipe_cleanup(css, pipe); - ipu3_dmamap_free(imgu, &css->xmem_sp_group_ptrs); - ipu3_css_fw_cleanup(css); + imgu_css_pipe_cleanup(css, pipe); + imgu_dmamap_free(imgu, &css->xmem_sp_group_ptrs); + imgu_css_fw_cleanup(css); } -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length) { struct imgu_device *imgu = dev_get_drvdata(dev); @@ -1556,35 +1556,35 @@ int ipu3_css_init(struct device *dev, struct ipu3_css *css, css->iomem_length = length; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; css_pipe->vf_output_en = false; spin_lock_init(&css_pipe->qlock); css_pipe->bindex = IPU3_CSS_DEFAULT_BINARY; css_pipe->pipe_id = IPU3_CSS_PIPE_ID_VIDEO; for (q = 0; q < IPU3_CSS_QUEUES; q++) { - r = ipu3_css_queue_init(&css_pipe->queue[q], NULL, 0); + r = imgu_css_queue_init(&css_pipe->queue[q], NULL, 0); if (r) return r; } - r = ipu3_css_map_init(css, pipe); + r = imgu_css_map_init(css, pipe); if (r) { - ipu3_css_cleanup(css); + imgu_css_cleanup(css); return r; } } - if (!ipu3_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, + if (!imgu_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, sizeof(struct imgu_abi_sp_group))) return -ENOMEM; - r = ipu3_css_fw_init(css); + r = imgu_css_fw_init(css); if (r) return r; return 0; } -static u32 ipu3_css_adjust(u32 res, u32 align) +static u32 imgu_css_adjust(u32 res, u32 align) { u32 val = max_t(u32, IPU3_CSS_MIN_RES, res); @@ -1592,9 +1592,9 @@ static u32 ipu3_css_adjust(u32 res, u32 align) } /* Select a binary matching the required resolutions and formats */ -static int ipu3_css_find_binary(struct ipu3_css *css, +static int imgu_css_find_binary(struct imgu_css *css, unsigned int pipe, - struct ipu3_css_queue queue[IPU3_CSS_QUEUES], + struct imgu_css_queue queue[IPU3_CSS_QUEUES], struct v4l2_rect rects[IPU3_CSS_RECTS]) { const int binary_nr = css->fwp->file_header.binary_nr; @@ -1611,7 +1611,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, const char *name; int i, j; - if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) + if (!imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) return -EINVAL; /* Find out the strip size boundary */ @@ -1659,7 +1659,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, in->height > bi->info.isp.sp.input.max_height) continue; - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { if (bi->info.isp.num_output_pins <= 0) continue; @@ -1681,7 +1681,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, continue; } - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { if (bi->info.isp.num_output_pins <= 1) continue; @@ -1716,7 +1716,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, * found binary number. May modify the given parameters if not exact match * is found. */ -int ipu3_css_fmt_try(struct ipu3_css *css, +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1744,14 +1744,14 @@ int ipu3_css_fmt_try(struct ipu3_css *css, struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS]; struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE]; struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC]; - struct ipu3_css_queue q[IPU3_CSS_QUEUES]; + struct imgu_css_queue q[IPU3_CSS_QUEUES]; struct v4l2_pix_format_mplane *const in = &q[IPU3_CSS_QUEUE_IN].fmt.mpix; struct v4l2_pix_format_mplane *const out = &q[IPU3_CSS_QUEUE_OUT].fmt.mpix; struct v4l2_pix_format_mplane *const vf = &q[IPU3_CSS_QUEUE_VF].fmt.mpix; - int i, s; + int i, s, ret; /* Adjust all formats, get statistics buffer sizes and formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { @@ -1762,7 +1762,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, else dev_dbg(css->dev, "%s %s: (not set)\n", __func__, qnames[i]); - if (ipu3_css_queue_init(&q[i], fmts[i], + if (imgu_css_queue_init(&q[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_notice(css->dev, "can not initialize queue %s\n", qnames[i]); @@ -1785,13 +1785,13 @@ int ipu3_css_fmt_try(struct ipu3_css *css, } /* Always require one input and vf only if out is also enabled */ - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || - !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || + !imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { dev_warn(css->dev, "required queues are disabled\n"); return -EINVAL; } - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { out->width = in->width; out->height = in->height; } @@ -1808,30 +1808,30 @@ int ipu3_css_fmt_try(struct ipu3_css *css, gdc->height = out->height; } - in->width = ipu3_css_adjust(in->width, 1); - in->height = ipu3_css_adjust(in->height, 1); - eff->width = ipu3_css_adjust(eff->width, EFF_ALIGN_W); - eff->height = ipu3_css_adjust(eff->height, 1); - bds->width = ipu3_css_adjust(bds->width, BDS_ALIGN_W); - bds->height = ipu3_css_adjust(bds->height, 1); - gdc->width = ipu3_css_adjust(gdc->width, OUT_ALIGN_W); - gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H); - out->width = ipu3_css_adjust(out->width, OUT_ALIGN_W); - out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H); - vf->width = ipu3_css_adjust(vf->width, VF_ALIGN_W); - vf->height = ipu3_css_adjust(vf->height, 1); + in->width = imgu_css_adjust(in->width, 1); + in->height = imgu_css_adjust(in->height, 1); + eff->width = imgu_css_adjust(eff->width, EFF_ALIGN_W); + eff->height = imgu_css_adjust(eff->height, 1); + bds->width = imgu_css_adjust(bds->width, BDS_ALIGN_W); + bds->height = imgu_css_adjust(bds->height, 1); + gdc->width = imgu_css_adjust(gdc->width, OUT_ALIGN_W); + gdc->height = imgu_css_adjust(gdc->height, OUT_ALIGN_H); + out->width = imgu_css_adjust(out->width, OUT_ALIGN_W); + out->height = imgu_css_adjust(out->height, OUT_ALIGN_H); + vf->width = imgu_css_adjust(vf->width, VF_ALIGN_W); + vf->height = imgu_css_adjust(vf->height, 1); s = (bds->width - gdc->width) / 2 - FILTER_SIZE; env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; s = (bds->height - gdc->height) / 2 - FILTER_SIZE; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - css->pipes[pipe].bindex = - ipu3_css_find_binary(css, pipe, q, r); - if (css->pipes[pipe].bindex < 0) { + ret = imgu_css_find_binary(css, pipe, q, r); + if (ret < 0) { dev_err(css->dev, "failed to find suitable binary\n"); return -EINVAL; } + css->pipes[pipe].bindex = ret; dev_dbg(css->dev, "Binary index %d for pipe %d found.", css->pipes[pipe].bindex, pipe); @@ -1839,7 +1839,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, /* Final adjustment and set back the queried formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { if (fmts[i]) { - if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix, + if (imgu_css_queue_init(&q[i], &q[i].fmt.mpix, IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_err(css->dev, "final resolution adjustment failed\n"); @@ -1862,7 +1862,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, return 0; } -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1870,7 +1870,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, struct v4l2_rect rect_data[IPU3_CSS_RECTS]; struct v4l2_rect *all_rects[IPU3_CSS_RECTS]; int i, r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (i = 0; i < IPU3_CSS_RECTS; i++) { if (rects[i]) @@ -1879,12 +1879,12 @@ int ipu3_css_fmt_set(struct ipu3_css *css, memset(&rect_data[i], 0, sizeof(rect_data[i])); all_rects[i] = &rect_data[i]; } - r = ipu3_css_fmt_try(css, fmts, all_rects, pipe); + r = imgu_css_fmt_try(css, fmts, all_rects, pipe); if (r < 0) return r; for (i = 0; i < IPU3_CSS_QUEUES; i++) - if (ipu3_css_queue_init(&css_pipe->queue[i], fmts[i], + if (imgu_css_queue_init(&css_pipe->queue[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) return -EINVAL; for (i = 0; i < IPU3_CSS_RECTS; i++) { @@ -1896,7 +1896,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, return 0; } -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt) { switch (fmt->dataformat) { case V4L2_META_FMT_IPU3_PARAMS: @@ -1913,27 +1913,27 @@ int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) } /* - * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first + * Queue given buffer to CSS. imgu_css_buf_prepare() must have been first * called for the buffer. May be called from interrupt context. * Returns 0 on success, -EBUSY if the buffer queue is full, or some other * code on error conditions. */ -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b) +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b) { struct imgu_abi_buffer *abi_buf; struct imgu_addr_t *buf_addr; u32 data; int r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; if (!css->streaming) return -EPROTO; /* CSS or buffer in wrong state */ - if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid) + if (b->queue >= IPU3_CSS_QUEUES || !imgu_css_queues[b->queue].qid) return -EINVAL; - b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid, + b->queue_pos = imgu_css_queue_pos(css, imgu_css_queues[b->queue].qid, pipe); if (b->queue_pos >= ARRAY_SIZE(css->pipes[pipe].abi_buffers[b->queue])) @@ -1943,7 +1943,7 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, /* Fill struct abi_buffer for firmware */ memset(abi_buf, 0, sizeof(*abi_buf)); - buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs; + buf_addr = (void *)abi_buf + imgu_css_queues[b->queue].ptr_ofs; *(imgu_addr_t *)buf_addr = b->daddr; if (b->queue == IPU3_CSS_QUEUE_STAT_3A) @@ -1963,14 +1963,14 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, b->state = IPU3_CSS_BUFFER_QUEUED; data = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].daddr; - r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid, + r = imgu_css_queue_data(css, imgu_css_queues[b->queue].qid, pipe, data); if (r < 0) goto queueing_failed; data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, - ipu3_css_queues[b->queue].qid); - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); + imgu_css_queues[b->queue].qid); + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); if (r < 0) goto queueing_failed; @@ -1992,7 +1992,7 @@ queueing_failed: * should be called again, or -EBUSY which means that there are no more * buffers available. May be called from interrupt context. */ -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css) { static const unsigned char evtype_to_queue[] = { [IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN, @@ -2000,15 +2000,15 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) [IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF, [IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A, }; - struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN); + struct imgu_css_buffer *b = ERR_PTR(-EAGAIN); u32 event, daddr; int evtype, pipe, pipeid, queue, qid, r; - struct ipu3_css_pipe *css_pipe; + struct imgu_css_pipe *css_pipe; if (!css->streaming) return ERR_PTR(-EPROTO); - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2025,7 +2025,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >> IMGU_ABI_EVTTYPE_PIPEID_SHIFT; queue = evtype_to_queue[evtype]; - qid = ipu3_css_queues[queue].qid; + qid = imgu_css_queues[queue].qid; if (pipe >= IMGU_MAX_PIPE_NUM) { dev_err(css->dev, "Invalid pipe: %i\n", pipe); @@ -2041,14 +2041,14 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) "event: buffer done 0x%x queue %i pipe %i pipeid %i\n", event, queue, pipe, pipeid); - r = ipu3_css_dequeue_data(css, qid, &daddr); + r = imgu_css_dequeue_data(css, qid, &daddr); if (r < 0) { dev_err(css->dev, "failed to dequeue buffer\n"); /* Force real error, not -EBUSY */ return ERR_PTR(-EIO); } - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid)); if (r < 0) { dev_err(css->dev, "failed to queue event\n"); @@ -2062,7 +2062,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) return ERR_PTR(-EIO); } b = list_first_entry(&css_pipe->queue[queue].bufs, - struct ipu3_css_buffer, list); + struct imgu_css_buffer, list); if (queue != b->queue || daddr != css_pipe->abi_buffers [b->queue][b->queue_pos].daddr) { @@ -2090,7 +2090,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) event, pipe); break; case IMGU_ABI_EVTTYPE_TIMER: - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2128,11 +2128,11 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) * Return index to css->parameter_set_info which has the newly created * parameters or negative value on error. */ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params) { static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const int stage = 0; const struct imgu_fw_info *bi; int obgrid_size; @@ -2144,7 +2144,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc = NULL; struct imgu_abi_gdc_warp_param *gdc = NULL; struct ipu3_uapi_obgrid_param *obgrid = NULL; - const struct ipu3_css_map *map; + const struct imgu_css_map *map; void *vmem0 = NULL; void *dmem0 = NULL; @@ -2157,7 +2157,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, dev_dbg(css->dev, "%s for pipe %d", __func__, pipe); bi = &css->fwp->binary_header[css_pipe->bindex]; - obgrid_size = ipu3_css_fw_obgrid_size(bi); + obgrid_size = imgu_css_fw_obgrid_size(bi); stripes = bi->info.isp.sp.iterator.num_stripes ? : 1; /* @@ -2165,45 +2165,45 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, * parameters from previous buffers will be overwritten. Fix the driver * not to allow this. */ - ipu3_css_pool_get(&css_pipe->pool.parameter_set_info); - param_set = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, + imgu_css_pool_get(&css_pipe->pool.parameter_set_info); + param_set = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0)->vaddr; /* Get a new acc only if new parameters given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); if (set_params || !map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.acc); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + imgu_css_pool_get(&css_pipe->pool.acc); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); acc = map->vaddr; } /* Get new VMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params || set_params->use.tnr3_vmem_params || set_params->use.xnr3_vmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); vmem0 = map->vaddr; } /* Get new DMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params || set_params->use.xnr3_dmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); dmem0 = map->vaddr; } /* Configure acc parameter cluster */ if (acc) { /* get acc_old */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 1); + map = imgu_css_pool_last(&css_pipe->pool.acc, 1); /* user acc */ - r = ipu3_css_cfg_acc(css, pipe, use, acc, map->vaddr, + r = imgu_css_cfg_acc(css, pipe, use, acc, map->vaddr, set_params ? &set_params->acc_param : NULL); if (r < 0) goto fail; @@ -2212,8 +2212,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure late binding parameters */ if (vmem0) { m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_vmem0(css, pipe, use, vmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_vmem0(css, pipe, use, vmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2221,8 +2221,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, if (dmem0) { m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_dmem0(css, pipe, use, dmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_dmem0(css, pipe, use, dmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2234,12 +2234,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, unsigned int g = IPU3_CSS_RECT_GDC; unsigned int e = IPU3_CSS_RECT_ENVELOPE; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); if (!map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.gdc); - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + imgu_css_pool_get(&css_pipe->pool.gdc); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); gdc = map->vaddr; - ipu3_css_cfg_gdc_table(map->vaddr, + imgu_css_cfg_gdc_table(map->vaddr, css_pipe->aux_frames[a].bytesperline / css_pipe->aux_frames[a].bytesperpixel, css_pipe->aux_frames[a].height, @@ -2252,10 +2252,10 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, } /* Get a new obgrid only if a new obgrid is given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); if (!map->vaddr || (set_params && set_params->use.obgrid_param)) { - ipu3_css_pool_get(&css_pipe->pool.obgrid); - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + imgu_css_pool_get(&css_pipe->pool.obgrid); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); obgrid = map->vaddr; /* Configure optical black level grid (obgrid) */ @@ -2269,30 +2269,30 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure parameter set info, queued to `queue_id' */ memset(param_set, 0, sizeof(*param_set)); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); param_set->mem_map.acc_cluster_params_for_sp = map->daddr; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); param_set->mem_map.dvs_6axis_params_y = map->daddr; for (i = 0; i < stripes; i++) { - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); param_set->mem_map.obgrid_tbl[i] = map->daddr + (obgrid_size / stripes) * i; } for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) { - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); param_set->mem_map.isp_mem_param[stage][m] = map->daddr; } /* Then queue the new parameter buffer */ - map = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, 0); - r = ipu3_css_queue_data(css, queue_id, pipe, map->daddr); + map = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0); + r = imgu_css_queue_data(css, queue_id, pipe, map->daddr); if (r < 0) goto fail; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, queue_id)); if (r < 0) @@ -2303,12 +2303,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, do { u32 daddr; - r = ipu3_css_dequeue_data(css, queue_id, &daddr); + r = imgu_css_dequeue_data(css, queue_id, &daddr); if (r == -EBUSY) break; if (r) goto fail_no_put; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED (queue_id)); if (r < 0) { @@ -2326,19 +2326,19 @@ fail: * parameters again later. */ - ipu3_css_pool_put(&css_pipe->pool.parameter_set_info); + imgu_css_pool_put(&css_pipe->pool.parameter_set_info); if (acc) - ipu3_css_pool_put(&css_pipe->pool.acc); + imgu_css_pool_put(&css_pipe->pool.acc); if (gdc) - ipu3_css_pool_put(&css_pipe->pool.gdc); + imgu_css_pool_put(&css_pipe->pool.gdc); if (obgrid) - ipu3_css_pool_put(&css_pipe->pool.obgrid); + imgu_css_pool_put(&css_pipe->pool.obgrid); if (vmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_VMEM0]); if (dmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_DMEM0]); @@ -2346,7 +2346,7 @@ fail_no_put: return r; } -int ipu3_css_irq_ack(struct ipu3_css *css) +int imgu_css_irq_ack(struct imgu_css *css) { static const int NUM_SWIRQS = 3; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h index e88d60f1a0c3..6b8bab27ab1f 100644 --- a/drivers/staging/media/ipu3/ipu3-css.h +++ b/drivers/staging/media/ipu3/ipu3-css.h @@ -43,7 +43,7 @@ * The pipe id type, distinguishes the kind of pipes that * can be run in parallel. */ -enum ipu3_css_pipe_id { +enum imgu_css_pipe_id { IPU3_CSS_PIPE_ID_PREVIEW, IPU3_CSS_PIPE_ID_COPY, IPU3_CSS_PIPE_ID_VIDEO, @@ -53,29 +53,29 @@ enum ipu3_css_pipe_id { IPU3_CSS_PIPE_ID_NUM }; -struct ipu3_css_resolution { +struct imgu_css_resolution { u32 w; u32 h; }; -enum ipu3_css_buffer_state { +enum imgu_css_buffer_state { IPU3_CSS_BUFFER_NEW, /* Not yet queued */ IPU3_CSS_BUFFER_QUEUED, /* Queued, waiting to be filled */ IPU3_CSS_BUFFER_DONE, /* Finished processing, removed from queue */ IPU3_CSS_BUFFER_FAILED, /* Was not processed, removed from queue */ }; -struct ipu3_css_buffer { +struct imgu_css_buffer { /* Private fields: user doesn't touch */ dma_addr_t daddr; unsigned int queue; - enum ipu3_css_buffer_state state; + enum imgu_css_buffer_state state; struct list_head list; u8 queue_pos; unsigned int pipe; }; -struct ipu3_css_format { +struct imgu_css_format { u32 pixelformat; enum v4l2_colorspace colorspace; enum imgu_abi_frame_format frame_format; @@ -89,22 +89,22 @@ struct ipu3_css_format { u8 flags; }; -struct ipu3_css_queue { +struct imgu_css_queue { union { struct v4l2_pix_format_mplane mpix; struct v4l2_meta_format meta; } fmt; - const struct ipu3_css_format *css_fmt; + const struct imgu_css_format *css_fmt; unsigned int width_pad; struct list_head bufs; }; -struct ipu3_css_pipe { - enum ipu3_css_pipe_id pipe_id; +struct imgu_css_pipe { + enum imgu_css_pipe_id pipe_id; unsigned int bindex; - struct ipu3_css_queue queue[IPU3_CSS_QUEUES]; + struct imgu_css_queue queue[IPU3_CSS_QUEUES]; struct v4l2_rect rect[IPU3_CSS_RECTS]; bool vf_output_en; @@ -112,21 +112,21 @@ struct ipu3_css_pipe { spinlock_t qlock; /* Data structures shared with IMGU and driver, always allocated */ - struct ipu3_css_map sp_ddr_ptrs; - struct ipu3_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map sp_ddr_ptrs; + struct imgu_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; - struct ipu3_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; /* * Data structures shared with IMGU and driver, binary specific. * PARAM_CLASS_CONFIG and PARAM_CLASS_STATE parameters. */ - struct ipu3_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] + struct imgu_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] [IMGU_ABI_NUM_MEMORIES]; struct { - struct ipu3_css_map mem[IPU3_CSS_AUX_FRAMES]; + struct imgu_css_map mem[IPU3_CSS_AUX_FRAMES]; unsigned int width; unsigned int height; unsigned int bytesperline; @@ -134,76 +134,76 @@ struct ipu3_css_pipe { } aux_frames[IPU3_CSS_AUX_FRAME_TYPES]; struct { - struct ipu3_css_pool parameter_set_info; - struct ipu3_css_pool acc; - struct ipu3_css_pool gdc; - struct ipu3_css_pool obgrid; + struct imgu_css_pool parameter_set_info; + struct imgu_css_pool acc; + struct imgu_css_pool gdc; + struct imgu_css_pool obgrid; /* PARAM_CLASS_PARAM parameters for binding while streaming */ - struct ipu3_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; + struct imgu_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; } pool; - struct ipu3_css_map abi_buffers[IPU3_CSS_QUEUES] + struct imgu_css_map abi_buffers[IPU3_CSS_QUEUES] [IMGU_ABI_HOST2SP_BUFQ_SIZE]; }; /* IPU3 Camera Sub System structure */ -struct ipu3_css { +struct imgu_css { struct device *dev; void __iomem *base; const struct firmware *fw; struct imgu_fw_header *fwp; int iomem_length; int fw_bl, fw_sp[IMGU_NUM_SP]; /* Indices of bl and SP binaries */ - struct ipu3_css_map *binary; /* fw binaries mapped to device */ + struct imgu_css_map *binary; /* fw binaries mapped to device */ bool streaming; /* true when streaming is enabled */ - struct ipu3_css_pipe pipes[IMGU_MAX_PIPE_NUM]; - struct ipu3_css_map xmem_sp_group_ptrs; + struct imgu_css_pipe pipes[IMGU_MAX_PIPE_NUM]; + struct imgu_css_map xmem_sp_group_ptrs; /* enabled pipe(s) */ DECLARE_BITMAP(enabled_pipes, IMGU_MAX_PIPE_NUM); }; /******************* css v4l *******************/ -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length); -void ipu3_css_cleanup(struct ipu3_css *css); -int ipu3_css_fmt_try(struct ipu3_css *css, +void imgu_css_cleanup(struct imgu_css *css); +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt); -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b); -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css); -int ipu3_css_start_streaming(struct ipu3_css *css); -void ipu3_css_stop_streaming(struct ipu3_css *css); -bool ipu3_css_queue_empty(struct ipu3_css *css); -bool ipu3_css_is_streaming(struct ipu3_css *css); -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe); +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt); +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b); +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css); +int imgu_css_start_streaming(struct imgu_css *css); +void imgu_css_stop_streaming(struct imgu_css *css); +bool imgu_css_queue_empty(struct imgu_css *css); +bool imgu_css_is_streaming(struct imgu_css *css); +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe); /******************* css hw *******************/ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base); -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base); -int ipu3_css_irq_ack(struct ipu3_css *css); +int imgu_css_set_powerup(struct device *dev, void __iomem *base); +void imgu_css_set_powerdown(struct device *dev, void __iomem *base); +int imgu_css_irq_ack(struct imgu_css *css); /******************* set parameters ************/ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params); /******************* auxiliary helpers *******************/ -static inline enum ipu3_css_buffer_state -ipu3_css_buf_state(struct ipu3_css_buffer *b) +static inline enum imgu_css_buffer_state +imgu_css_buf_state(struct imgu_css_buffer *b) { return b->state; } /* Initialize given buffer. May be called several times. */ -static inline void ipu3_css_buf_init(struct ipu3_css_buffer *b, +static inline void imgu_css_buf_init(struct imgu_css_buffer *b, unsigned int queue, dma_addr_t daddr) { b->state = IPU3_CSS_BUFFER_NEW; diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index 93a393d4e15e..d978a00e1e0b 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -12,11 +12,12 @@ #include "ipu3.h" #include "ipu3-css-pool.h" #include "ipu3-mmu.h" +#include "ipu3-dmamap.h" /* - * Free a buffer allocated by ipu3_dmamap_alloc_buffer() + * Free a buffer allocated by imgu_dmamap_alloc_buffer() */ -static void ipu3_dmamap_free_buffer(struct page **pages, +static void imgu_dmamap_free_buffer(struct page **pages, size_t size) { int count = size >> PAGE_SHIFT; @@ -30,7 +31,7 @@ static void ipu3_dmamap_free_buffer(struct page **pages, * Based on the implementation of __iommu_dma_alloc_pages() * defined in drivers/iommu/dma-iommu.c */ -static struct page **ipu3_dmamap_alloc_buffer(size_t size, +static struct page **imgu_dmamap_alloc_buffer(size_t size, unsigned long order_mask, gfp_t gfp) { @@ -73,7 +74,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, __free_pages(page, order); } if (!page) { - ipu3_dmamap_free_buffer(pages, i << PAGE_SHIFT); + imgu_dmamap_free_buffer(pages, i << PAGE_SHIFT); return NULL; } count -= order_size; @@ -85,7 +86,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, } /** - * ipu3_dmamap_alloc - allocate and map a buffer into KVA + * imgu_dmamap_alloc - allocate and map a buffer into KVA * @imgu: struct device pointer * @map: struct to store mapping variables * @len: size required @@ -94,7 +95,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, * KVA on success * %NULL on failure */ -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len) { unsigned long shift = iova_shift(&imgu->iova_domain); @@ -113,7 +114,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, if (!iova) return NULL; - pages = ipu3_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, + pages = imgu_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, GFP_KERNEL); if (!pages) goto out_free_iova; @@ -121,7 +122,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, /* Call IOMMU driver to setup pgt */ iovaddr = iova_dma_addr(&imgu->iova_domain, iova); for (i = 0; i < size / PAGE_SIZE; ++i) { - rval = ipu3_mmu_map(imgu->mmu, iovaddr, + rval = imgu_mmu_map(imgu->mmu, iovaddr, page_to_phys(pages[i]), PAGE_SIZE); if (rval) goto out_unmap; @@ -152,8 +153,8 @@ out_vunmap: vunmap(map->vma->addr); out_unmap: - ipu3_dmamap_free_buffer(pages, size); - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_dmamap_free_buffer(pages, size); + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), i * PAGE_SIZE); map->vma = NULL; @@ -163,7 +164,7 @@ out_free_iova: return NULL; } -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map) { struct iova *iova; @@ -172,16 +173,16 @@ void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) if (WARN_ON(!iova)) return; - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), iova_size(iova) << iova_shift(&imgu->iova_domain)); __free_iova(&imgu->iova_domain, iova); } /* - * Counterpart of ipu3_dmamap_alloc + * Counterpart of imgu_dmamap_alloc */ -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map) { struct vm_struct *area = map->vma; @@ -191,18 +192,18 @@ void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) if (!map->vaddr) return; - ipu3_dmamap_unmap(imgu, map); + imgu_dmamap_unmap(imgu, map); if (WARN_ON(!area) || WARN_ON(!area->pages)) return; - ipu3_dmamap_free_buffer(area->pages, map->size); + imgu_dmamap_free_buffer(area->pages, map->size); vunmap(map->vaddr); map->vaddr = NULL; } -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map) +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map) { unsigned long shift = iova_shift(&imgu->iova_domain); struct scatterlist *sg; @@ -232,7 +233,7 @@ int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, iova->pfn_hi); - if (ipu3_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + if (imgu_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), sglist, nents) < size) goto out_fail; @@ -248,7 +249,7 @@ out_fail: return -EFAULT; } -int ipu3_dmamap_init(struct imgu_device *imgu) +int imgu_dmamap_init(struct imgu_device *imgu) { unsigned long order, base_pfn; int ret = iova_cache_get(); @@ -263,7 +264,7 @@ int ipu3_dmamap_init(struct imgu_device *imgu) return 0; } -void ipu3_dmamap_exit(struct imgu_device *imgu) +void imgu_dmamap_exit(struct imgu_device *imgu) { put_iova_domain(&imgu->iova_domain); iova_cache_put(); diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.h b/drivers/staging/media/ipu3/ipu3-dmamap.h index b9d224a33273..9db513b3d603 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.h +++ b/drivers/staging/media/ipu3/ipu3-dmamap.h @@ -8,15 +8,15 @@ struct imgu_device; struct scatterlist; -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len); -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map); +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map); -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map); +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map); +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_init(struct imgu_device *imgu); -void ipu3_dmamap_exit(struct imgu_device *imgu); +int imgu_dmamap_init(struct imgu_device *imgu); +void imgu_dmamap_exit(struct imgu_device *imgu); #endif diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index b9f209541f78..cfc2bdfb14b3 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -48,7 +48,7 @@ #define REG_GP_HALT (IMGU_REG_BASE + 0x5dc) #define REG_GP_HALTED (IMGU_REG_BASE + 0x5e0) -struct ipu3_mmu { +struct imgu_mmu { struct device *dev; void __iomem *base; /* protect access to l2pts, l1pt */ @@ -63,28 +63,28 @@ struct ipu3_mmu { u32 **l2pts; u32 *l1pt; - struct ipu3_mmu_info geometry; + struct imgu_mmu_info geometry; }; -static inline struct ipu3_mmu *to_ipu3_mmu(struct ipu3_mmu_info *info) +static inline struct imgu_mmu *to_imgu_mmu(struct imgu_mmu_info *info) { - return container_of(info, struct ipu3_mmu, geometry); + return container_of(info, struct imgu_mmu, geometry); } /** - * ipu3_mmu_tlb_invalidate - invalidate translation look-aside buffer + * imgu_mmu_tlb_invalidate - invalidate translation look-aside buffer * @mmu: MMU to perform the invalidate operation on * * This function invalidates the whole TLB. Must be called when the hardware * is powered on. */ -static void ipu3_mmu_tlb_invalidate(struct ipu3_mmu *mmu) +static void imgu_mmu_tlb_invalidate(struct imgu_mmu *mmu) { writel(TLB_INVALIDATE, mmu->base + REG_TLB_INVALIDATE); } -static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, - void (*func)(struct ipu3_mmu *mmu)) +static void call_if_imgu_is_powered(struct imgu_mmu *mmu, + void (*func)(struct imgu_mmu *mmu)) { if (!pm_runtime_get_if_in_use(mmu->dev)) return; @@ -94,14 +94,14 @@ static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, } /** - * ipu3_mmu_set_halt - set CIO gate halt bit + * imgu_mmu_set_halt - set CIO gate halt bit * @mmu: MMU to set the CIO gate bit in. * @halt: Desired state of the gate bit. * * This function sets the CIO gate bit that controls whether external memory * accesses are allowed. Must be called when the hardware is powered on. */ -static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) +static void imgu_mmu_set_halt(struct imgu_mmu *mmu, bool halt) { int ret; u32 val; @@ -116,12 +116,12 @@ static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) } /** - * ipu3_mmu_alloc_page_table - allocate a pre-filled page table + * imgu_mmu_alloc_page_table - allocate a pre-filled page table * @pteval: Value to initialize for page table entries with. * * Return: Pointer to allocated page table or NULL on failure. */ -static u32 *ipu3_mmu_alloc_page_table(u32 pteval) +static u32 *imgu_mmu_alloc_page_table(u32 pteval) { u32 *pt; int pte; @@ -139,10 +139,10 @@ static u32 *ipu3_mmu_alloc_page_table(u32 pteval) } /** - * ipu3_mmu_free_page_table - free page table + * imgu_mmu_free_page_table - free page table * @pt: Page table to free. */ -static void ipu3_mmu_free_page_table(u32 *pt) +static void imgu_mmu_free_page_table(u32 *pt) { set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER); free_page((unsigned long)pt); @@ -168,7 +168,7 @@ static inline void address_to_pte_idx(unsigned long iova, u32 *l1pt_idx, *l1pt_idx = iova & IPU3_L1PT_MASK; } -static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) +static u32 *imgu_mmu_get_l2pt(struct imgu_mmu *mmu, u32 l1pt_idx) { unsigned long flags; u32 *l2pt, *new_l2pt; @@ -182,7 +182,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) spin_unlock_irqrestore(&mmu->lock, flags); - new_l2pt = ipu3_mmu_alloc_page_table(mmu->dummy_page_pteval); + new_l2pt = imgu_mmu_alloc_page_table(mmu->dummy_page_pteval); if (!new_l2pt) return NULL; @@ -193,7 +193,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) l2pt = mmu->l2pts[l1pt_idx]; if (l2pt) { - ipu3_mmu_free_page_table(new_l2pt); + imgu_mmu_free_page_table(new_l2pt); goto done; } @@ -208,7 +208,7 @@ done: return l2pt; } -static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, +static int __imgu_mmu_map(struct imgu_mmu *mmu, unsigned long iova, phys_addr_t paddr) { u32 l1pt_idx, l2pt_idx; @@ -220,7 +220,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx); - l2pt = ipu3_mmu_get_l2pt(mmu, l1pt_idx); + l2pt = imgu_mmu_get_l2pt(mmu, l1pt_idx); if (!l2pt) return -ENOMEM; @@ -238,11 +238,11 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, return 0; } -/** +/* * The following four functions are implemented based on iommu.c * drivers/iommu/iommu.c/iommu_pgsize(). */ -static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, +static size_t imgu_mmu_pgsize(unsigned long pgsize_bitmap, unsigned long addr_merge, size_t size) { unsigned int pgsize_idx; @@ -276,10 +276,10 @@ static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, } /* drivers/iommu/iommu.c/iommu_map() */ -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); unsigned int min_pagesz; int ret = 0; @@ -301,13 +301,13 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, iova, &paddr, size); while (size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova | paddr, size); dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", iova, &paddr, pgsize); - ret = __ipu3_mmu_map(mmu, iova, paddr); + ret = __imgu_mmu_map(mmu, iova, paddr); if (ret) break; @@ -316,16 +316,16 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, size -= pgsize; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return ret; } /* drivers/iommu/iommu.c/default_iommu_map_sg() */ -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); struct scatterlist *s; size_t s_length, mapped = 0; unsigned int i, min_pagesz; @@ -345,25 +345,25 @@ size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz)) s_length = PAGE_ALIGN(s->length); - ret = ipu3_mmu_map(info, iova + mapped, phys, s_length); + ret = imgu_mmu_map(info, iova + mapped, phys, s_length); if (ret) goto out_err; mapped += s_length; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return mapped; out_err: /* undo mappings already done */ - ipu3_mmu_unmap(info, iova, mapped); + imgu_mmu_unmap(info, iova, mapped); return 0; } -static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, +static size_t __imgu_mmu_unmap(struct imgu_mmu *mmu, unsigned long iova, size_t size) { u32 l1pt_idx, l2pt_idx; @@ -395,10 +395,10 @@ static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, } /* drivers/iommu/iommu.c/iommu_unmap() */ -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; @@ -423,10 +423,10 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, * or we hit an area that isn't mapped. */ while (unmapped < size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova, size - unmapped); - unmapped_page = __ipu3_mmu_unmap(mmu, iova, pgsize); + unmapped_page = __imgu_mmu_unmap(mmu, iova, pgsize); if (!unmapped_page) break; @@ -437,20 +437,21 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, unmapped += unmapped_page; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return unmapped; } /** - * ipu3_mmu_init() - initialize IPU3 MMU block + * imgu_mmu_init() - initialize IPU3 MMU block + * @parent: struct device parent * @base: IOMEM base of hardware registers. * * Return: Pointer to IPU3 MMU private data pointer or ERR_PTR() on error. */ -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base) { - struct ipu3_mmu *mmu; + struct imgu_mmu *mmu; u32 pteval; mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); @@ -462,7 +463,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) spin_lock_init(&mmu->lock); /* Disallow external memory access when having no valid page tables. */ - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); /* * The MMU does not have a "valid" bit, so we have to use a dummy @@ -478,7 +479,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) * Allocate a dummy L2 page table with all entries pointing to * the dummy page. */ - mmu->dummy_l2pt = ipu3_mmu_alloc_page_table(pteval); + mmu->dummy_l2pt = imgu_mmu_alloc_page_table(pteval); if (!mmu->dummy_l2pt) goto fail_dummy_page; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_l2pt)); @@ -493,14 +494,14 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) goto fail_l2pt; /* Allocate the L1 page table. */ - mmu->l1pt = ipu3_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); + mmu->l1pt = imgu_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); if (!mmu->l1pt) goto fail_l2pts; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); mmu->geometry.aperture_start = 0; mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS); @@ -511,7 +512,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) fail_l2pts: vfree(mmu->l2pts); fail_l2pt: - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); fail_dummy_page: free_page((unsigned long)mmu->dummy_page); fail_group: @@ -521,41 +522,41 @@ fail_group: } /** - * ipu3_mmu_exit() - clean up IPU3 MMU block - * @mmu: IPU3 MMU private data + * imgu_mmu_exit() - clean up IPU3 MMU block + * @info: IPU3 MMU private data */ -void ipu3_mmu_exit(struct ipu3_mmu_info *info) +void imgu_mmu_exit(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); /* We are going to free our page tables, no more memory access. */ - ipu3_mmu_set_halt(mmu, true); - ipu3_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, true); + imgu_mmu_tlb_invalidate(mmu); - ipu3_mmu_free_page_table(mmu->l1pt); + imgu_mmu_free_page_table(mmu->l1pt); vfree(mmu->l2pts); - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); free_page((unsigned long)mmu->dummy_page); kfree(mmu); } -void ipu3_mmu_suspend(struct ipu3_mmu_info *info) +void imgu_mmu_suspend(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); } -void ipu3_mmu_resume(struct ipu3_mmu_info *info) +void imgu_mmu_resume(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); u32 pteval; - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); } diff --git a/drivers/staging/media/ipu3/ipu3-mmu.h b/drivers/staging/media/ipu3/ipu3-mmu.h index 8fe63b4c6e1c..fa58827eb19c 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.h +++ b/drivers/staging/media/ipu3/ipu3-mmu.h @@ -6,13 +6,13 @@ #define __IPU3_MMU_H /** - * struct ipu3_mmu_info - Describes mmu geometry + * struct imgu_mmu_info - Describes mmu geometry * * @aperture_start: First address that can be mapped * @aperture_end: Last address that can be mapped * @pgsize_bitmap: Bitmap of page sizes in use */ -struct ipu3_mmu_info { +struct imgu_mmu_info { dma_addr_t aperture_start; dma_addr_t aperture_end; unsigned long pgsize_bitmap; @@ -21,15 +21,15 @@ struct ipu3_mmu_info { struct device; struct scatterlist; -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base); -void ipu3_mmu_exit(struct ipu3_mmu_info *info); -void ipu3_mmu_suspend(struct ipu3_mmu_info *info); -void ipu3_mmu_resume(struct ipu3_mmu_info *info); +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base); +void imgu_mmu_exit(struct imgu_mmu_info *info); +void imgu_mmu_suspend(struct imgu_mmu_info *info); +void imgu_mmu_resume(struct imgu_mmu_info *info); -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size); -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size); -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents); #endif diff --git a/drivers/staging/media/ipu3/ipu3-tables.c b/drivers/staging/media/ipu3/ipu3-tables.c index 334517987eba..3a3730bd4395 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.c +++ b/drivers/staging/media/ipu3/ipu3-tables.c @@ -5,8 +5,8 @@ #define X 0 /* Don't care value */ -const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { +const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { /* Scale factor 32 / (32 + 0) = 1 */ .hor_phase_arr = { .even = { { 0, 0, 64, 6, 0, 0, 0 } }, @@ -9015,7 +9015,7 @@ const struct ipu3_css_bds_config .ver_ds_en = 1 } }; -const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { +const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000000000000000, IMGU_SCALER_FP * -0.000249009327023, IMGU_SCALER_FP * -0.001022241683322, @@ -9146,7 +9146,7 @@ const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000249009327023 }; -const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { +const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { IMGU_SCALER_FP * 0.074300676367033, IMGU_SCALER_FP * 0.094030234498392, IMGU_SCALER_FP * 0.115522859526596, @@ -9214,7 +9214,7 @@ const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { }; /* settings for Geometric Distortion Correction */ -const s16 ipu3_css_gdc_lut[4][256] = { { +const s16 imgu_css_gdc_lut[4][256] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -5, -5, -5, -6, -6, -7, -7, -7, -8, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, @@ -9292,7 +9292,7 @@ const s16 ipu3_css_gdc_lut[4][256] = { { -1, 0, 1, 0, 0, 0, 0, 0, 0, 0 } }; -const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { +const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults = { .x = { 1024, 1164, 1320, 1492, 1680, 1884, 2108, 2352, 2616, 2900, 3208, 3540, 3896, 4276, 4684, 5120 @@ -9311,7 +9311,7 @@ const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { }; /* settings for Bayer Noise Reduction */ -const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { +const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults = { { 16, 16, 16, 16 }, /* wb_gains */ { 16, 16, 16, 16 }, /* wb_gains_thr */ { 0, X, 8, 6, X, 14 }, /* thr_coeffs */ @@ -9327,18 +9327,18 @@ const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { { 8, 4, 4, X, 8, X, 1, 1, 1, 1 }, /* dn_detect_ctrl */ }; -const struct ipu3_uapi_dm_config ipu3_css_dm_defaults = { +const struct ipu3_uapi_dm_config imgu_css_dm_defaults = { 1, 1, 1, X, X, 8, X, 7, X, 8, X, 8, X, 4, X }; -const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults = { +const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults = { 9775, -2671, 1087, 0, -1071, 8303, 815, 0, -23, -7887, 16103, 0 }; /* settings for Gamma correction */ -const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { +const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut = { { 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287, 303, 319, 335, 351, 367, 383, 399, 415, 431, 447, 463, 479, 495, 511, 527, 543, 559, 575, 591, 607, 623, 639, 655, 671, 687, 703, 719, 735, @@ -9362,13 +9362,13 @@ const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { 7807, 7871, 7935, 7999, 8063, 8127, 8191 } }; -const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults = { +const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults = { 4898, 9617, 1867, 0, -2410, -4732, 7143, 0, 10076, -8437, -1638, 0 }; -const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { +const struct ipu3_uapi_cds_params imgu_css_cds_defaults = { 1, 3, 3, 1, 1, 3, 3, 1, 4, X, /* ds_nf */ @@ -9376,7 +9376,7 @@ const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { 0, X /* uv_bin_output */ }; -const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { +const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults = { .grid = { .width = 73, .height = 55, @@ -9397,7 +9397,7 @@ const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { }, }; -const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { +const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults = { .units = { .cu_1 = { 0, 150, 7, 0 }, .cu_ed = { 7, 110, 244, X, 307, 409, 511, X, @@ -9436,17 +9436,17 @@ const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { { 1, X, 2, X, 8, X } }, }; -const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults = { +const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults = { 0, 1, 1, 0, 0, 1, 1, 0, 2, X, 0, X }; -const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults = { +const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults = { .coring = { 0, X, 0, X }, .sense_gain = { 6, 6, 6, X, 4, 4, 4, X }, .iir_fir = { 8, X, 12, X, 0, 256 - 127, X }, }; -const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { +const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults = { .lpf = { 4, X, 8, X, 16, X, 0 }, .sense = { 8191, X, 0, X, 8191, X, 0, X }, .gain = { 8, X, 0, X, 8, X, 0, X }, @@ -9457,7 +9457,7 @@ const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { }; const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut = { { + imgu_css_tcc_gain_pcwl_lut = { { 1024, 1032, 1040, 1048, 1057, 1065, 1073, 1081, 1089, 1097, 1105, 1113, 1122, 1130, 1138, 1146, 1154, 1162, 1170, 1178, 1187, 1195, 1203, 1211, 1219, 1227, 1235, 1243, 1252, 1260, 1268, 1276, 1284, 1292, 1300, 1308, @@ -9483,12 +9483,12 @@ const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config } }; const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut = { { + imgu_css_tcc_r_sqr_lut = { { 32, 44, 64, 92, 128, 180, 256, 364, 512, 628, 724, 808, 888, 956, 1024, 1088, 1144, 1200, 1256, 1304, 1356, 1404, 1448 } }; -const struct imgu_abi_anr_config ipu3_css_anr_defaults = { +const struct imgu_abi_anr_config imgu_css_anr_defaults = { .transform = { .adaptive_treshhold_en = 1, .alpha = { { 13, 13, 13, 13, 0, 0, 0, 0}, @@ -9545,7 +9545,7 @@ const struct imgu_abi_anr_config ipu3_css_anr_defaults = { }; /* frame settings for Auto White Balance */ -const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { +const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults = { .grid_cfg = { .width = 16, .height = 16, @@ -9560,7 +9560,7 @@ const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { }; /* settings for Auto Exposure */ -const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { +const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults = { .width = 16, .height = 16, .block_width_log2 = 3, @@ -9571,13 +9571,13 @@ const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { }; /* settings for Auto Exposure color correction matrix */ -const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults = { +const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults = { 256, 256, 256, 256, /* gain_gr/r/b/gb */ .mat = { 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128 }, }; /* settings for Auto Focus */ -const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { +const struct ipu3_uapi_af_config_s imgu_css_af_defaults = { .filter_config = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, @@ -9595,7 +9595,7 @@ const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { }; /* settings for Auto White Balance */ -const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults = { +const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults = { 8191, 8191, 8191, 8191 | /* rgbs_thr_gr/r/gb/b */ IPU3_UAPI_AWB_RGBS_THR_B_EN | IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT, .grid = { diff --git a/drivers/staging/media/ipu3/ipu3-tables.h b/drivers/staging/media/ipu3/ipu3-tables.h index 6563782cbd22..a1bf3286f380 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.h +++ b/drivers/staging/media/ipu3/ipu3-tables.h @@ -19,7 +19,7 @@ #define IMGU_GDC_LUT_UNIT 4 #define IMGU_GDC_LUT_LEN 256 -struct ipu3_css_bds_config { +struct imgu_css_bds_config { struct imgu_abi_bds_phase_arr hor_phase_arr; struct imgu_abi_bds_phase_arr ver_phase_arr; struct imgu_abi_bds_ptrn_arr ptrn_arr; @@ -28,39 +28,39 @@ struct ipu3_css_bds_config { u8 ver_ds_en; }; -struct ipu3_css_xnr3_vmem_defaults { +struct imgu_css_xnr3_vmem_defaults { s16 x[IMGU_XNR3_VMEM_LUT_LEN]; s16 a[IMGU_XNR3_VMEM_LUT_LEN]; s16 b[IMGU_XNR3_VMEM_LUT_LEN]; s16 c[IMGU_XNR3_VMEM_LUT_LEN]; }; -extern const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN]; -extern const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; -extern const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; -extern const s16 ipu3_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; -extern const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults; -extern const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults; -extern const struct ipu3_uapi_dm_config ipu3_css_dm_defaults; -extern const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults; -extern const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut; -extern const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults; -extern const struct ipu3_uapi_cds_params ipu3_css_cds_defaults; -extern const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults; -extern const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults; -extern const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults; -extern const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults; -extern const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults; +extern const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN]; +extern const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; +extern const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; +extern const s16 imgu_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; +extern const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults; +extern const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults; +extern const struct ipu3_uapi_dm_config imgu_css_dm_defaults; +extern const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults; +extern const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut; +extern const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults; +extern const struct ipu3_uapi_cds_params imgu_css_cds_defaults; +extern const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults; +extern const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults; +extern const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults; +extern const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults; +extern const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults; extern const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut; + imgu_css_tcc_gain_pcwl_lut; extern const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut; -extern const struct imgu_abi_anr_config ipu3_css_anr_defaults; -extern const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults; -extern const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults; -extern const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults; -extern const struct ipu3_uapi_af_config_s ipu3_css_af_defaults; -extern const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults; + imgu_css_tcc_r_sqr_lut; +extern const struct imgu_abi_anr_config imgu_css_anr_defaults; +extern const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults; +extern const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults; +extern const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults; +extern const struct ipu3_uapi_af_config_s imgu_css_af_defaults; +extern const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults; #endif diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index c7936032beb9..9c0352b193a7 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -12,7 +12,10 @@ /******************** v4l2_subdev_ops ********************/ -static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +#define IPU3_RUNNING_MODE_VIDEO 0 +#define IPU3_RUNNING_MODE_STILL 1 + +static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imgu_v4l2_subdev *imgu_sd = container_of(sd, struct imgu_v4l2_subdev, @@ -47,7 +50,7 @@ static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) +static int imgu_subdev_s_stream(struct v4l2_subdev *sd, int enable) { int i; unsigned int node; @@ -60,7 +63,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) struct device *dev = &imgu->pci_dev->dev; struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES] = { NULL }; struct v4l2_rect *rects[IPU3_CSS_RECTS] = { NULL }; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; dev_dbg(dev, "%s %d for pipe %d", __func__, enable, pipe); @@ -104,7 +107,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) rects[IPU3_CSS_RECT_BDS] = &imgu_sd->rect.bds; rects[IPU3_CSS_RECT_GDC] = &imgu_sd->rect.gdc; - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); if (r) { dev_err(dev, "failed to set initial formats pipe %d with (%d)", pipe, r); @@ -116,7 +119,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -140,7 +143,7 @@ static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -186,7 +189,7 @@ static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, +static int imgu_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -219,7 +222,7 @@ static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, +static int imgu_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -260,7 +263,7 @@ static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, /******************** media_entity_operations ********************/ -static int ipu3_link_setup(struct media_entity *entity, +static int imgu_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { @@ -299,7 +302,7 @@ static int ipu3_link_setup(struct media_entity *entity, /******************** vb2_ops ********************/ -static int ipu3_vb2_buf_init(struct vb2_buffer *vb) +static int imgu_vb2_buf_init(struct vb2_buffer *vb) { struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); @@ -312,11 +315,11 @@ static int ipu3_vb2_buf_init(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return 0; - return ipu3_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); + return imgu_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); } /* Called when each buffer is freed */ -static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) +static void imgu_vb2_buf_cleanup(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_buffer *buf = container_of(vb, @@ -328,11 +331,11 @@ static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return; - ipu3_dmamap_unmap(imgu, &buf->map); + imgu_dmamap_unmap(imgu, &buf->map); } /* Transfer buffer ownership to me */ -static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) +static void imgu_vb2_buf_queue(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_video_device *node = @@ -358,7 +361,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vb2_set_plane_payload(vb, 0, payload); } if (payload >= need_bytes) - r = ipu3_css_set_parameters(&imgu->css, pipe, + r = imgu_css_set_parameters(&imgu->css, pipe, vb2_plane_vaddr(vb, 0)); buf->flags = V4L2_BUF_FLAG_DONE; vb2_buffer_done(vb, r == 0 ? VB2_BUF_STATE_DONE @@ -369,7 +372,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vid_buf.vbb.vb2_buf); mutex_lock(&imgu->lock); - ipu3_css_buf_init(&buf->css_buf, queue, buf->map.daddr); + imgu_css_buf_init(&buf->css_buf, queue, buf->map.daddr); list_add_tail(&buf->vid_buf.list, &node->buffers); mutex_unlock(&imgu->lock); @@ -385,7 +388,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) } -static int ipu3_vb2_queue_setup(struct vb2_queue *vq, +static int imgu_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], @@ -422,7 +425,7 @@ static int ipu3_vb2_queue_setup(struct vb2_queue *vq, } /* Check if all enabled video nodes are streaming, exception ignored */ -static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, +static bool imgu_all_nodes_streaming(struct imgu_device *imgu, struct imgu_video_device *except) { unsigned int i, pipe, p; @@ -451,11 +454,11 @@ static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, return true; } -static void ipu3_return_all_buffers(struct imgu_device *imgu, +static void imgu_return_all_buffers(struct imgu_device *imgu, struct imgu_video_device *node, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b, *b0; + struct imgu_vb2_buffer *b, *b0; /* Return all buffers */ mutex_lock(&imgu->lock); @@ -466,7 +469,7 @@ static void ipu3_return_all_buffers(struct imgu_device *imgu, mutex_unlock(&imgu->lock); } -static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +static int imgu_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -497,7 +500,7 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) goto fail_return_bufs; - if (!ipu3_all_nodes_streaming(imgu, node)) + if (!imgu_all_nodes_streaming(imgu, node)) return 0; for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) { @@ -518,12 +521,12 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) fail_stop_pipeline: media_pipeline_stop(&node->vdev.entity); fail_return_bufs: - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); return r; } -static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) +static void imgu_vb2_stop_streaming(struct vb2_queue *vq) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -544,7 +547,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) "failed to stop subdev streaming\n"); /* Was this the first node with streaming disabled? */ - if (imgu->streaming && ipu3_all_nodes_streaming(imgu, node)) { + if (imgu->streaming && imgu_all_nodes_streaming(imgu, node)) { /* Yes, really stop streaming now */ dev_dbg(dev, "IMGU streaming is ready to stop"); r = imgu_s_stream(imgu, false); @@ -552,7 +555,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) imgu->streaming = false; } - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); media_pipeline_stop(&node->vdev.entity); } @@ -563,13 +566,13 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) #define DEF_VID_CAPTURE 0 #define DEF_VID_OUTPUT 1 -struct ipu3_fmt { +struct imgu_fmt { u32 fourcc; u16 type; /* VID_CAPTURE or VID_OUTPUT not both */ }; /* format descriptions for capture and preview */ -static const struct ipu3_fmt formats[] = { +static const struct imgu_fmt formats[] = { { V4L2_PIX_FMT_NV12, VID_CAPTURE }, { V4L2_PIX_FMT_IPU3_SGRBG10, VID_OUTPUT }, { V4L2_PIX_FMT_IPU3_SBGGR10, VID_OUTPUT }, @@ -578,7 +581,7 @@ static const struct ipu3_fmt formats[] = { }; /* Find the first matched format, return default if not found */ -static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) +static const struct imgu_fmt *find_format(struct v4l2_format *f, u32 type) { unsigned int i; @@ -592,10 +595,10 @@ static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) &formats[DEF_VID_OUTPUT]; } -static int ipu3_vidioc_querycap(struct file *file, void *fh, +static int imgu_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); strscpy(cap->driver, IMGU_NAME, sizeof(cap->driver)); strscpy(cap->card, IMGU_NAME, sizeof(cap->card)); @@ -643,10 +646,10 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, } /* Propagate forward always the format from the CIO2 subdev */ -static int ipu3_vidioc_g_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); f->fmt = node->vdev_fmt.fmt; @@ -667,7 +670,7 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, struct v4l2_mbus_framefmt pad_fmt; unsigned int i, css_q; int r; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; struct imgu_v4l2_subdev *imgu_sd = &imgu_pipe->imgu_sd; @@ -733,9 +736,9 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return -EINVAL; if (try) - r = ipu3_css_fmt_try(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_try(&imgu->css, fmts, rects, pipe); else - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); /* r is the binary number in the firmware blob */ if (r < 0) @@ -749,10 +752,10 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return 0; } -static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; - const struct ipu3_fmt *fmt; + const struct imgu_fmt *fmt; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fmt = find_format(f, VID_CAPTURE); @@ -769,58 +772,58 @@ static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) return 0; } -static int ipu3_vidioc_try_fmt(struct file *file, void *fh, +static int imgu_vidioc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, true); } -static int ipu3_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, false); } -struct ipu3_meta_fmt { +struct imgu_meta_fmt { __u32 fourcc; char *name; }; /* From drivers/media/v4l2-core/v4l2-ioctl.c */ -static const struct ipu3_meta_fmt meta_fmts[] = { +static const struct imgu_meta_fmt meta_fmts[] = { { V4L2_META_FMT_IPU3_PARAMS, "IPU3 processing parameters" }, { V4L2_META_FMT_IPU3_STAT_3A, "IPU3 3A statistics" }, }; -static int ipu3_meta_enum_format(struct file *file, void *fh, +static int imgu_meta_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); unsigned int i = fmt->type == V4L2_BUF_TYPE_META_OUTPUT ? 0 : 1; /* Each node is dedicated to only one meta format */ @@ -833,10 +836,10 @@ static int ipu3_meta_enum_format(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_meta_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); if (f->type != node->vbq.type) return -EINVAL; @@ -846,7 +849,7 @@ static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_enum_input(struct file *file, void *fh, +static int imgu_vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *input) { if (input->index > 0) @@ -857,19 +860,19 @@ static int ipu3_vidioc_enum_input(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_input(struct file *file, void *fh, unsigned int *input) +static int imgu_vidioc_g_input(struct file *file, void *fh, unsigned int *input) { *input = 0; return 0; } -static int ipu3_vidioc_s_input(struct file *file, void *fh, unsigned int input) +static int imgu_vidioc_s_input(struct file *file, void *fh, unsigned int input) { return input == 0 ? 0 : -EINVAL; } -static int ipu3_vidioc_enum_output(struct file *file, void *fh, +static int imgu_vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *output) { if (output->index > 0) @@ -880,7 +883,7 @@ static int ipu3_vidioc_enum_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_output(struct file *file, void *fh, +static int imgu_vidioc_g_output(struct file *file, void *fh, unsigned int *output) { *output = 0; @@ -888,7 +891,7 @@ static int ipu3_vidioc_g_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_s_output(struct file *file, void *fh, +static int imgu_vidioc_s_output(struct file *file, void *fh, unsigned int output) { return output == 0 ? 0 : -EINVAL; @@ -896,54 +899,54 @@ static int ipu3_vidioc_s_output(struct file *file, void *fh, /******************** function pointers ********************/ -static struct v4l2_subdev_internal_ops ipu3_subdev_internal_ops = { - .open = ipu3_subdev_open, +static struct v4l2_subdev_internal_ops imgu_subdev_internal_ops = { + .open = imgu_subdev_open, }; -static const struct v4l2_subdev_core_ops ipu3_subdev_core_ops = { +static const struct v4l2_subdev_core_ops imgu_subdev_core_ops = { .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops ipu3_subdev_video_ops = { - .s_stream = ipu3_subdev_s_stream, +static const struct v4l2_subdev_video_ops imgu_subdev_video_ops = { + .s_stream = imgu_subdev_s_stream, }; -static const struct v4l2_subdev_pad_ops ipu3_subdev_pad_ops = { +static const struct v4l2_subdev_pad_ops imgu_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .get_fmt = ipu3_subdev_get_fmt, - .set_fmt = ipu3_subdev_set_fmt, - .get_selection = ipu3_subdev_get_selection, - .set_selection = ipu3_subdev_set_selection, + .get_fmt = imgu_subdev_get_fmt, + .set_fmt = imgu_subdev_set_fmt, + .get_selection = imgu_subdev_get_selection, + .set_selection = imgu_subdev_set_selection, }; -static const struct v4l2_subdev_ops ipu3_subdev_ops = { - .core = &ipu3_subdev_core_ops, - .video = &ipu3_subdev_video_ops, - .pad = &ipu3_subdev_pad_ops, +static const struct v4l2_subdev_ops imgu_subdev_ops = { + .core = &imgu_subdev_core_ops, + .video = &imgu_subdev_video_ops, + .pad = &imgu_subdev_pad_ops, }; -static const struct media_entity_operations ipu3_media_ops = { - .link_setup = ipu3_link_setup, +static const struct media_entity_operations imgu_media_ops = { + .link_setup = imgu_link_setup, .link_validate = v4l2_subdev_link_validate, }; /****************** vb2_ops of the Q ********************/ -static const struct vb2_ops ipu3_vb2_ops = { - .buf_init = ipu3_vb2_buf_init, - .buf_cleanup = ipu3_vb2_buf_cleanup, - .buf_queue = ipu3_vb2_buf_queue, - .queue_setup = ipu3_vb2_queue_setup, - .start_streaming = ipu3_vb2_start_streaming, - .stop_streaming = ipu3_vb2_stop_streaming, +static const struct vb2_ops imgu_vb2_ops = { + .buf_init = imgu_vb2_buf_init, + .buf_cleanup = imgu_vb2_buf_cleanup, + .buf_queue = imgu_vb2_buf_queue, + .queue_setup = imgu_vb2_queue_setup, + .start_streaming = imgu_vb2_start_streaming, + .stop_streaming = imgu_vb2_stop_streaming, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; /****************** v4l2_file_operations *****************/ -static const struct v4l2_file_operations ipu3_v4l2_fops = { +static const struct v4l2_file_operations imgu_v4l2_fops = { .unlocked_ioctl = video_ioctl2, .open = v4l2_fh_open, .release = vb2_fop_release, @@ -953,26 +956,26 @@ static const struct v4l2_file_operations ipu3_v4l2_fops = { /******************** v4l2_ioctl_ops ********************/ -static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_cap_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_cap_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_cap_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = imgu_vidioc_try_fmt, .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_out_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_out_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_out_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_out_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_out_mplane = imgu_vidioc_try_fmt, - .vidioc_enum_output = ipu3_vidioc_enum_output, - .vidioc_g_output = ipu3_vidioc_g_output, - .vidioc_s_output = ipu3_vidioc_s_output, + .vidioc_enum_output = imgu_vidioc_enum_output, + .vidioc_g_output = imgu_vidioc_g_output, + .vidioc_s_output = imgu_vidioc_s_output, - .vidioc_enum_input = ipu3_vidioc_enum_input, - .vidioc_g_input = ipu3_vidioc_g_input, - .vidioc_s_input = ipu3_vidioc_s_input, + .vidioc_enum_input = imgu_vidioc_enum_input, + .vidioc_g_input = imgu_vidioc_g_input, + .vidioc_s_input = imgu_vidioc_s_input, /* buffer queue management */ .vidioc_reqbufs = vb2_ioctl_reqbufs, @@ -986,20 +989,20 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_meta_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, /* meta capture */ - .vidioc_enum_fmt_meta_cap = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_cap = imgu_meta_enum_format, + .vidioc_g_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_cap = imgu_vidioc_g_meta_fmt, /* meta output */ - .vidioc_enum_fmt_meta_out = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_out = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_out = imgu_meta_enum_format, + .vidioc_g_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_out = imgu_vidioc_g_meta_fmt, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -1012,7 +1015,7 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int imgu_sd_s_ctrl(struct v4l2_ctrl *ctrl) { struct imgu_v4l2_subdev *imgu_sd = container_of(ctrl->handler, struct imgu_v4l2_subdev, ctrl_handler); @@ -1031,25 +1034,29 @@ static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) } } -static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = { - .s_ctrl = ipu3_sd_s_ctrl, +static const struct v4l2_ctrl_ops imgu_subdev_ctrl_ops = { + .s_ctrl = imgu_sd_s_ctrl, +}; + +static const char * const imgu_ctrl_mode_strings[] = { + "Video mode", + "Still mode", }; -static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = { - .ops = &ipu3_subdev_ctrl_ops, +static const struct v4l2_ctrl_config imgu_subdev_ctrl_mode = { + .ops = &imgu_subdev_ctrl_ops, .id = V4L2_CID_INTEL_IPU3_MODE, .name = "IPU3 Pipe Mode", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = IPU3_RUNNING_MODE_VIDEO, - .max = IPU3_RUNNING_MODE_STILL, - .step = 1, + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(imgu_ctrl_mode_strings) - 1, .def = IPU3_RUNNING_MODE_VIDEO, + .qmenu = imgu_ctrl_mode_strings, }; /******************** Framework registration ********************/ /* helper function to config node's video properties */ -static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, +static void imgu_node_to_v4l2(u32 node, struct video_device *vdev, struct v4l2_format *f) { u32 cap; @@ -1061,32 +1068,32 @@ static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, case IMGU_NODE_IN: cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; break; case IMGU_NODE_PARAMS: cap = V4L2_CAP_META_OUTPUT; f->type = V4L2_BUF_TYPE_META_OUTPUT; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_PARAMS; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; case IMGU_NODE_STAT_3A: cap = V4L2_CAP_META_CAPTURE; f->type = V4L2_BUF_TYPE_META_CAPTURE; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_STAT_3A; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; default: cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; } vdev->device_caps = V4L2_CAP_STREAMING | cap; } -static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, +static int imgu_v4l2_subdev_register(struct imgu_device *imgu, struct imgu_v4l2_subdev *imgu_sd, unsigned int pipe) { @@ -1102,16 +1109,16 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, "failed initialize subdev media entity (%d)\n", r); return r; } - imgu_sd->subdev.entity.ops = &ipu3_media_ops; + imgu_sd->subdev.entity.ops = &imgu_media_ops; for (i = 0; i < IMGU_NODE_NUM; i++) { imgu_sd->subdev_pads[i].flags = imgu_pipe->nodes[i].output ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; } /* Initialize subdev */ - v4l2_subdev_init(&imgu_sd->subdev, &ipu3_subdev_ops); + v4l2_subdev_init(&imgu_sd->subdev, &imgu_subdev_ops); imgu_sd->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS; - imgu_sd->subdev.internal_ops = &ipu3_subdev_internal_ops; + imgu_sd->subdev.internal_ops = &imgu_subdev_internal_ops; imgu_sd->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(imgu_sd->subdev.name, sizeof(imgu_sd->subdev.name), @@ -1120,7 +1127,7 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, atomic_set(&imgu_sd->running_mode, IPU3_RUNNING_MODE_VIDEO); v4l2_ctrl_handler_init(hdl, 1); imgu_sd->subdev.ctrl_handler = hdl; - imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &ipu3_subdev_ctrl_mode, NULL); + imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &imgu_subdev_ctrl_mode, NULL); if (hdl->error) { r = hdl->error; dev_err(&imgu->pci_dev->dev, @@ -1144,7 +1151,7 @@ fail_subdev: return r; } -static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, +static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, int node_num) { int r; @@ -1189,7 +1196,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, node->pad_fmt = def_bus_fmt; node->id = node_num; node->pipe = pipe; - ipu3_node_to_v4l2(node_num, vdev, &node->vdev_fmt); + imgu_node_to_v4l2(node_num, vdev, &node->vdev_fmt); if (node->vdev_fmt.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || node->vdev_fmt.type == @@ -1214,11 +1221,11 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, /* Initialize vbq */ vbq->type = node->vdev_fmt.type; vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; - vbq->ops = &ipu3_vb2_ops; + vbq->ops = &imgu_vb2_ops; vbq->mem_ops = &vb2_dma_sg_memops; if (imgu->buf_struct_size <= 0) imgu->buf_struct_size = - sizeof(struct ipu3_vb2_buffer); + sizeof(struct imgu_vb2_buffer); vbq->buf_struct_size = imgu->buf_struct_size; vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; /* can streamon w/o buffers */ @@ -1236,7 +1243,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, snprintf(vdev->name, sizeof(vdev->name), "%s %d %s", IMGU_NAME, pipe, node->name); vdev->release = video_device_release_empty; - vdev->fops = &ipu3_v4l2_fops; + vdev->fops = &imgu_v4l2_fops; vdev->lock = &node->lock; vdev->v4l2_dev = &imgu->v4l2_dev; vdev->queue = &node->vbq; @@ -1269,7 +1276,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, return 0; } -static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, +static void imgu_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, unsigned int pipe, int node) { int i; @@ -1282,12 +1289,12 @@ static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, } } -static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) +static int imgu_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) { int i, r; for (i = 0; i < IMGU_NODE_NUM; i++) { - r = ipu3_v4l2_node_setup(imgu, pipe, i); + r = imgu_v4l2_node_setup(imgu, pipe, i); if (r) goto cleanup; } @@ -1295,11 +1302,11 @@ static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) return 0; cleanup: - ipu3_v4l2_nodes_cleanup_pipe(imgu, pipe, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, pipe, i); return r; } -static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) +static void imgu_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) { struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[i]; @@ -1308,13 +1315,13 @@ static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) media_entity_cleanup(&imgu_pipe->imgu_sd.subdev.entity); } -static void ipu3_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) +static void imgu_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) { int i; for (i = 0; i < pipe; i++) { - ipu3_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); + imgu_v4l2_subdev_cleanup(imgu, i); } } @@ -1325,15 +1332,15 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) for (i = 0; i < IMGU_MAX_PIPE_NUM; i++) { imgu_pipe = &imgu->imgu_pipe[i]; - r = ipu3_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); + r = imgu_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); if (r) { dev_err(&imgu->pci_dev->dev, "failed to register subdev%d ret (%d)\n", i, r); goto pipes_cleanup; } - r = ipu3_v4l2_nodes_setup_pipe(imgu, i); + r = imgu_v4l2_nodes_setup_pipe(imgu, i); if (r) { - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_subdev_cleanup(imgu, i); goto pipes_cleanup; } } @@ -1341,7 +1348,7 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) return 0; pipes_cleanup: - ipu3_v4l2_cleanup_pipes(imgu, i); + imgu_v4l2_cleanup_pipes(imgu, i); return r; } @@ -1389,7 +1396,7 @@ int imgu_v4l2_register(struct imgu_device *imgu) return 0; fail_subdevs: - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); fail_v4l2_pipes: v4l2_device_unregister(&imgu->v4l2_dev); fail_v4l2_dev: @@ -1401,7 +1408,7 @@ fail_v4l2_dev: int imgu_v4l2_unregister(struct imgu_device *imgu) { media_device_unregister(&imgu->media_dev); - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); v4l2_device_unregister(&imgu->v4l2_dev); media_device_cleanup(&imgu->media_dev); @@ -1411,8 +1418,8 @@ int imgu_v4l2_unregister(struct imgu_device *imgu) void imgu_v4l2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b = - container_of(vb, struct ipu3_vb2_buffer, vbb.vb2_buf); + struct imgu_vb2_buffer *b = + container_of(vb, struct imgu_vb2_buffer, vbb.vb2_buf); list_del(&b->list); vb2_buffer_done(&b->vbb.vb2_buf, state); diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index d521b3afb8b1..d575ac78c8f0 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -72,7 +72,7 @@ static void imgu_dummybufs_cleanup(struct imgu_device *imgu, unsigned int pipe) struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; for (i = 0; i < IPU3_CSS_QUEUES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &imgu_pipe->queues[i].dmap); } @@ -93,7 +93,7 @@ static int imgu_dummybufs_preallocate(struct imgu_device *imgu, if (i == IMGU_QUEUE_MASTER || size == 0) continue; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); return -ENOMEM; @@ -133,7 +133,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) else size = mpix->plane_fmt[0].sizeimage; - if (ipu3_css_dma_buffer_resize(imgu, + if (imgu_css_dma_buffer_resize(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); @@ -141,7 +141,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } for (k = 0; k < IMGU_MAX_QUEUE_DEPTH; k++) - ipu3_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, + imgu_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, imgu_pipe->queues[i].dmap.daddr); } @@ -149,7 +149,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } /* May be called from atomic context */ -static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, int queue, unsigned int pipe) { unsigned int i; @@ -164,14 +164,14 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, return NULL; for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++) - if (ipu3_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != + if (imgu_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != IPU3_CSS_BUFFER_QUEUED) break; if (i == IMGU_MAX_QUEUE_DEPTH) return NULL; - ipu3_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, + imgu_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, imgu_pipe->queues[queue].dmap.daddr); return &imgu_pipe->queues[queue].dummybufs[i]; @@ -179,7 +179,7 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, /* Check if given buffer is a dummy buffer */ static bool imgu_dummybufs_check(struct imgu_device *imgu, - struct ipu3_css_buffer *buf, + struct imgu_css_buffer *buf, unsigned int pipe) { unsigned int i; @@ -200,7 +200,7 @@ static void imgu_buffer_done(struct imgu_device *imgu, struct vb2_buffer *vb, mutex_unlock(&imgu->lock); } -static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, unsigned int node, unsigned int pipe) { @@ -212,7 +212,7 @@ static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, /* Find first free buffer from the node */ list_for_each_entry(buf, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) return &buf->css_buf; } @@ -230,7 +230,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe int r = 0; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; - if (!ipu3_css_is_streaming(&imgu->css)) + if (!imgu_css_is_streaming(&imgu->css)) return 0; dev_dbg(&imgu->pci_dev->dev, "Queue buffers to pipe %d", pipe); @@ -247,7 +247,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe "Vf not enabled, ignore queue"); continue; } else if (imgu_pipe->queue_enabled[node]) { - struct ipu3_css_buffer *buf = + struct imgu_css_buffer *buf = imgu_queue_getbuf(imgu, node, pipe); struct imgu_buffer *ibuf = NULL; bool dummy; @@ -255,7 +255,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe if (!buf) break; - r = ipu3_css_buf_queue(&imgu->css, pipe, buf); + r = imgu_css_buf_queue(&imgu->css, pipe, buf); if (r) break; dummy = imgu_dummybufs_check(imgu, buf, pipe); @@ -300,7 +300,7 @@ failed: list_for_each_entry_safe(buf, buf0, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_QUEUED) continue; /* Was already queued, skip */ @@ -317,18 +317,18 @@ static int imgu_powerup(struct imgu_device *imgu) { int r; - r = ipu3_css_set_powerup(&imgu->pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base); if (r) return r; - ipu3_mmu_resume(imgu->mmu); + imgu_mmu_resume(imgu->mmu); return 0; } static void imgu_powerdown(struct imgu_device *imgu) { - ipu3_mmu_suspend(imgu->mmu); - ipu3_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); + imgu_mmu_suspend(imgu->mmu); + imgu_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); } int imgu_s_stream(struct imgu_device *imgu, int enable) @@ -341,7 +341,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) dev_dbg(dev, "stream off\n"); /* Block new buffers to be queued to CSS. */ atomic_set(&imgu->qbuf_barrier, 1); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); synchronize_irq(imgu->pci_dev->irq); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); @@ -366,7 +366,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to start css streaming (%d)", r); goto fail_start_streaming; @@ -393,7 +393,7 @@ fail_queueing: for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) imgu_dummybufs_cleanup(imgu, pipe); fail_dummybufs: - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); fail_start_streaming: pm_runtime_put(dev); @@ -435,7 +435,7 @@ static int imgu_video_nodes_init(struct imgu_device *imgu) rects[IPU3_CSS_RECT_EFFECTIVE] = &imgu_pipe->imgu_sd.rect.eff; rects[IPU3_CSS_RECT_BDS] = &imgu_pipe->imgu_sd.rect.bds; - ipu3_css_fmt_set(&imgu->css, fmts, rects, j); + imgu_css_fmt_set(&imgu->css, fmts, rects, j); /* Pre-allocate dummy buffers */ r = imgu_dummybufs_preallocate(imgu, j); @@ -478,23 +478,22 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) /* Dequeue / queue buffers */ do { u64 ns = ktime_get_ns(); - struct ipu3_css_buffer *b; + struct imgu_css_buffer *b; struct imgu_buffer *buf = NULL; unsigned int node, pipe; bool dummy; do { mutex_lock(&imgu->lock); - b = ipu3_css_buf_dequeue(&imgu->css); + b = imgu_css_buf_dequeue(&imgu->css); mutex_unlock(&imgu->lock); } while (PTR_ERR(b) == -EAGAIN); - if (IS_ERR_OR_NULL(b)) { - if (!b || PTR_ERR(b) == -EBUSY) /* All done */ - break; - dev_err(&imgu->pci_dev->dev, - "failed to dequeue buffers (%ld)\n", - PTR_ERR(b)); + if (IS_ERR(b)) { + if (PTR_ERR(b) != -EBUSY) /* All done */ + dev_err(&imgu->pci_dev->dev, + "failed to dequeue buffers (%ld)\n", + PTR_ERR(b)); break; } @@ -526,12 +525,12 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) buf->vid_buf.vbb.sequence); } imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf, - ipu3_css_buf_state(&buf->css_buf) == + imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_DONE ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); mutex_lock(&imgu->lock); - if (ipu3_css_queue_empty(&imgu->css)) + if (imgu_css_queue_empty(&imgu->css)) wake_up_all(&imgu->buf_drain_wq); mutex_unlock(&imgu->lock); } while (1); @@ -553,7 +552,7 @@ static irqreturn_t imgu_isr(int irq, void *imgu_ptr) struct imgu_device *imgu = imgu_ptr; /* acknowledge interruption */ - if (ipu3_css_irq_ack(&imgu->css) < 0) + if (imgu_css_irq_ack(&imgu->css) < 0) return IRQ_NONE; return IRQ_WAKE_THREAD; @@ -638,21 +637,21 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, atomic_set(&imgu->qbuf_barrier, 0); init_waitqueue_head(&imgu->buf_drain_wq); - r = ipu3_css_set_powerup(&pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&pci_dev->dev, imgu->base); if (r) { dev_err(&pci_dev->dev, "failed to power up CSS (%d)\n", r); goto out_mutex_destroy; } - imgu->mmu = ipu3_mmu_init(&pci_dev->dev, imgu->base); + imgu->mmu = imgu_mmu_init(&pci_dev->dev, imgu->base); if (IS_ERR(imgu->mmu)) { r = PTR_ERR(imgu->mmu); dev_err(&pci_dev->dev, "failed to initialize MMU (%d)\n", r); goto out_css_powerdown; } - r = ipu3_dmamap_init(imgu); + r = imgu_dmamap_init(imgu); if (r) { dev_err(&pci_dev->dev, "failed to initialize DMA mapping (%d)\n", r); @@ -660,7 +659,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, } /* ISP programming */ - r = ipu3_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); + r = imgu_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); if (r) { dev_err(&pci_dev->dev, "failed to initialize CSS (%d)\n", r); goto out_dmamap_exit; @@ -690,13 +689,13 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, out_video_exit: imgu_video_nodes_exit(imgu); out_css_cleanup: - ipu3_css_cleanup(&imgu->css); + imgu_css_cleanup(&imgu->css); out_dmamap_exit: - ipu3_dmamap_exit(imgu); + imgu_dmamap_exit(imgu); out_mmu_exit: - ipu3_mmu_exit(imgu->mmu); + imgu_mmu_exit(imgu->mmu); out_css_powerdown: - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); out_mutex_destroy: mutex_destroy(&imgu->lock); @@ -711,10 +710,10 @@ static void imgu_pci_remove(struct pci_dev *pci_dev) pm_runtime_get_noresume(&pci_dev->dev); imgu_video_nodes_exit(imgu); - ipu3_css_cleanup(&imgu->css); - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); - ipu3_dmamap_exit(imgu); - ipu3_mmu_exit(imgu->mmu); + imgu_css_cleanup(&imgu->css); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_dmamap_exit(imgu); + imgu_mmu_exit(imgu->mmu); mutex_destroy(&imgu->lock); } @@ -724,7 +723,7 @@ static int __maybe_unused imgu_suspend(struct device *dev) struct imgu_device *imgu = pci_get_drvdata(pci_dev); dev_dbg(dev, "enter %s\n", __func__); - imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css); + imgu->suspend_in_stream = imgu_css_is_streaming(&imgu->css); if (!imgu->suspend_in_stream) goto out; /* Block new buffers to be queued to CSS. */ @@ -736,10 +735,10 @@ static int __maybe_unused imgu_suspend(struct device *dev) synchronize_irq(pci_dev->irq); /* Wait until all buffers in CSS are done. */ if (!wait_event_timeout(imgu->buf_drain_wq, - ipu3_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) + imgu_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) dev_err(dev, "wait buffer drain timeout.\n"); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); pm_runtime_force_suspend(dev); @@ -769,7 +768,7 @@ static int __maybe_unused imgu_resume(struct device *dev) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to resume css streaming (%d)", r); goto out; diff --git a/drivers/staging/media/ipu3/ipu3.h b/drivers/staging/media/ipu3/ipu3.h index 04fc99f47ebb..73b123b2b8a2 100644 --- a/drivers/staging/media/ipu3/ipu3.h +++ b/drivers/staging/media/ipu3/ipu3.h @@ -32,7 +32,7 @@ #define IMGU_NODE_STAT_3A 4 /* 3A statistics */ #define IMGU_NODE_NUM 5 -#define file_to_intel_ipu3_node(__file) \ +#define file_to_intel_imgu_node(__file) \ container_of(video_devdata(__file), struct imgu_video_device, vdev) #define IPU3_INPUT_MIN_WIDTH 0U @@ -44,7 +44,7 @@ #define IPU3_OUTPUT_MAX_WIDTH 4480U #define IPU3_OUTPUT_MAX_HEIGHT 34004U -struct ipu3_vb2_buffer { +struct imgu_vb2_buffer { /* Public fields */ struct vb2_v4l2_buffer vbb; /* Must be the first field */ @@ -53,9 +53,9 @@ struct ipu3_vb2_buffer { }; struct imgu_buffer { - struct ipu3_vb2_buffer vid_buf; /* Must be the first field */ - struct ipu3_css_buffer css_buf; - struct ipu3_css_map map; + struct imgu_vb2_buffer vid_buf; /* Must be the first field */ + struct imgu_css_buffer css_buf; + struct imgu_css_map map; }; struct imgu_node_mapping { @@ -107,8 +107,8 @@ struct imgu_media_pipe { /* Internally enabled queues */ struct { - struct ipu3_css_map dmap; - struct ipu3_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; + struct imgu_css_map dmap; + struct imgu_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; } queues[IPU3_CSS_QUEUES]; struct imgu_video_device nodes[IMGU_NODE_NUM]; bool queue_enabled[IMGU_NODE_NUM]; @@ -135,18 +135,18 @@ struct imgu_device { struct v4l2_file_operations v4l2_file_ops; /* MMU driver for css */ - struct ipu3_mmu_info *mmu; + struct imgu_mmu_info *mmu; struct iova_domain iova_domain; /* css - Camera Sub-System */ - struct ipu3_css css; + struct imgu_css css; /* * Coarse-grained lock to protect * vid_buf.list and css->queue */ struct mutex lock; - /* Forbit streaming and buffer queuing during system suspend. */ + /* Forbid streaming and buffer queuing during system suspend. */ atomic_t qbuf_barrier; /* Indicate if system suspend take place while imgu is streaming. */ bool suspend_in_stream; diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 059cf5bd3c36..a6dc2d2b1228 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -712,7 +712,7 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2, /* Skip interrupts until we reach the frame skip count. The CSI2 will be * automatically disabled, as the frame skip count has been programmed - * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. + * in the CSI2_CTx_CTRL1::COUNT field, so re-enable it. * * It would have been nice to rely on the FRAME_NUMBER interrupt instead * but it turned out that the interrupt is only generated when the CSI2 diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c index 5282236d1bb1..06daea66fb49 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c @@ -80,7 +80,7 @@ rk3288_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -88,7 +88,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -99,7 +99,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENC_CTRL); rk3288_vpu_set_src_img_ctrl(vpu, ctx); - rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3288_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c index dbc86d95fe3b..3d438797692e 100644 --- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c @@ -111,7 +111,7 @@ rk3399_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -119,7 +119,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -130,7 +130,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENCODE_START); rk3399_vpu_set_src_img_ctrl(vpu, ctx); - rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig new file mode 100644 index 000000000000..bacd30f0348d --- /dev/null +++ b/drivers/staging/media/soc_camera/Kconfig @@ -0,0 +1,50 @@ +config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN + select VIDEOBUF2_CORE + help + SoC Camera is a common API to several cameras, not connecting + over a bus like PCI or USB. For example some i2c camera connected + directly to the data bus of an SoC. + +comment "soc_camera sensor drivers" + +config SOC_CAMERA_MT9M111 + tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" + depends on SOC_CAMERA && I2C + select VIDEO_MT9M111 + help + This driver supports MT9M111, MT9M112 and MT9M131 cameras from + Micron/Aptina. + This is the legacy configuration which shouldn't be used anymore, + while VIDEO_MT9M111 should be used instead. + +config SOC_CAMERA_MT9V022 + tristate "mt9v022 and mt9v024 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9V022 cameras from Micron + +config SOC_CAMERA_OV5642 + tristate "ov5642 camera support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV5642 sensor + +config SOC_CAMERA_OV9740 + tristate "ov9740 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9740 camera driver + +config SOC_CAMERA_IMX074 + tristate "imx074 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports IMX074 cameras from Sony + +config SOC_CAMERA_MT9T031 + tristate "mt9t031 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile new file mode 100644 index 000000000000..3a351bd629f5 --- /dev/null +++ b/drivers/staging/media/soc_camera/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o +obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o +obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/imx074/imx074.c b/drivers/staging/media/soc_camera/imx074.c index 1676c166dc83..1676c166dc83 100644 --- a/drivers/staging/media/imx074/imx074.c +++ b/drivers/staging/media/soc_camera/imx074.c diff --git a/drivers/staging/media/mt9t031/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c index 4ff179302b4f..4ff179302b4f 100644 --- a/drivers/staging/media/mt9t031/mt9t031.c +++ b/drivers/staging/media/soc_camera/mt9t031.c diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c new file mode 100644 index 000000000000..1ab86a7499b9 --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_camera.c @@ -0,0 +1,2170 @@ +/* + * camera image capture (abstract) bus driver + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This driver provides an interface between platform-specific camera + * buses and camera devices. It should be used if the camera is + * connected not over a "proper" bus like PCI or USB, but over a + * special bus, like, for example, the Quick Capture interface on PXA270 + * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. + * It can handle multiple cameras and / or multiple buses, which can + * be used, e.g., in stereo-vision applications. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <media/soc_camera.h> +#include <media/drv-intf/soc_mediabus.h> +#include <media/v4l2-async.h> +#include <media/v4l2-clk.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-fwnode.h> +#include <media/videobuf2-v4l2.h> + +/* Default to VGA resolution */ +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define MAP_MAX_NUM 32 +static DECLARE_BITMAP(device_map, MAP_MAX_NUM); +static LIST_HEAD(hosts); +static LIST_HEAD(devices); +/* + * Protects lists and bitmaps of hosts and devices. + * Lock nesting: Ok to take ->host_lock under list_lock. + */ +static DEFINE_MUTEX(list_lock); + +struct soc_camera_async_client { + struct v4l2_async_subdev *sensor; + struct v4l2_async_notifier notifier; + struct platform_device *pdev; + struct list_head list; /* needed for clean up */ +}; + +static int soc_camera_video_start(struct soc_camera_device *icd); +static int video_dev_create(struct soc_camera_device *icd); + +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) +{ + int ret; + bool clock_toggle; + + if (clk && (!ssdd->unbalanced_power || + !test_and_set_bit(0, &ssdd->clock_state))) { + ret = v4l2_clk_enable(clk); + if (ret < 0) { + dev_err(dev, "Cannot enable clock: %d\n", ret); + return ret; + } + clock_toggle = true; + } else { + clock_toggle = false; + } + + ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (ret < 0) { + dev_err(dev, "Cannot enable regulators\n"); + goto eregenable; + } + + if (ssdd->power) { + ret = ssdd->power(dev, 1); + if (ret < 0) { + dev_err(dev, + "Platform failed to power-on the camera.\n"); + goto epwron; + } + } + + return 0; + +epwron: + regulator_bulk_disable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); +eregenable: + if (clock_toggle) + v4l2_clk_disable(clk); + + return ret; +} +EXPORT_SYMBOL(soc_camera_power_on); + +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) +{ + int ret = 0; + int err; + + if (ssdd->power) { + err = ssdd->power(dev, 0); + if (err < 0) { + dev_err(dev, + "Platform failed to power-off the camera.\n"); + ret = err; + } + } + + err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (err < 0) { + dev_err(dev, "Cannot disable regulators\n"); + ret = ret ? : err; + } + + if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) + v4l2_clk_disable(clk); + + return ret; +} +EXPORT_SYMBOL(soc_camera_power_off); + +int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) +{ + /* Should not have any effect in synchronous case */ + return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); +} +EXPORT_SYMBOL(soc_camera_power_init); + +static int __soc_camera_power_on(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int __soc_camera_power_off(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + ret = v4l2_subdev_call(sd, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int soc_camera_clock_start(struct soc_camera_host *ici) +{ + int ret; + + if (!ici->ops->clock_start) + return 0; + + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + + return ret; +} + +static void soc_camera_clock_stop(struct soc_camera_host *ici) +{ + if (!ici->ops->clock_stop) + return; + + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); +} + +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->num_user_formats; i++) + if (icd->user_formats[i].host_fmt->fourcc == fourcc) + return icd->user_formats + i; + return NULL; +} +EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); + +/** + * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags + * @ssdd: camera platform parameters + * @cfg: media bus configuration + * @return: resulting flags + */ +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, + const struct v4l2_mbus_config *cfg) +{ + unsigned long f, flags = cfg->flags; + + /* If only one of the two polarities is supported, switch to the opposite */ + if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { + f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; + } + + if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { + f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; + } + + if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { + f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); + if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) + flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; + } + + return flags; +} +EXPORT_SYMBOL(soc_camera_apply_board_flags); + +#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ + ((x) >> 24) & 0xff + +static int soc_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { + pix->bytesperline = 0; + pix->sizeimage = 0; + } + + ret = ici->ops->try_fmt(icd, f); + if (ret < 0) + return ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) + return -EINVAL; + + ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; + + pix->bytesperline = max_t(u32, pix->bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = max_t(u32, pix->sizeimage, ret); + + return 0; +} + +static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + /* Only single-plane capture is supported so far */ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* limit format to hardware capabilities */ + return soc_camera_try_fmt(icd, f); +} + +static int soc_camera_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct soc_camera_device *icd = file->private_data; + + if (inp->index != 0) + return -EINVAL; + + /* default is camera */ + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = icd->vdev->tvnorms; + strscpy(inp->name, "Camera", sizeof(inp->name)); + + return 0; +} + +static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, s_std, a); +} + +static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, g_std, a); +} + +static int soc_camera_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + return ici->ops->enum_framesizes(icd, fsize); +} + +static int soc_camera_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int ret; + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_reqbufs(&icd->vb2_vidq, p); + if (!ret) + icd->streamer = p->count ? file : NULL; + return ret; +} + +static int soc_camera_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + return vb2_querybuf(&icd->vb2_vidq, p); +} + +static int soc_camera_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer != file) + return -EBUSY; + + return vb2_qbuf(&icd->vb2_vidq, NULL, p); +} + +static int soc_camera_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer != file) + return -EBUSY; + + return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); +} + +static int soc_camera_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct soc_camera_device *icd = file->private_data; + int ret; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_create_bufs(&icd->vb2_vidq, create); + if (!ret) + icd->streamer = file; + return ret; +} + +static int soc_camera_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); +} + +static int soc_camera_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + return vb2_expbuf(&icd->vb2_vidq, p); +} + +/* Always entered with .host_lock held */ +static int soc_camera_init_user_formats(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + unsigned int i, fmts = 0, raw_fmts = 0; + int ret; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + raw_fmts++; + code.index++; + } + + if (!ici->ops->get_formats) + /* + * Fallback mode - the host will have to serve all + * sensor-provided formats one-to-one to the user + */ + fmts = raw_fmts; + else + /* + * First pass - only count formats this host-sensor + * configuration can provide + */ + for (i = 0; i < raw_fmts; i++) { + ret = ici->ops->get_formats(icd, i, NULL); + if (ret < 0) + return ret; + fmts += ret; + } + + if (!fmts) + return -ENXIO; + + icd->user_formats = + vmalloc(array_size(fmts, + sizeof(struct soc_camera_format_xlate))); + if (!icd->user_formats) + return -ENOMEM; + + dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); + + /* Second pass - actually fill data formats */ + fmts = 0; + for (i = 0; i < raw_fmts; i++) + if (!ici->ops->get_formats) { + code.index = i; + v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); + icd->user_formats[fmts].host_fmt = + soc_mbus_get_fmtdesc(code.code); + if (icd->user_formats[fmts].host_fmt) + icd->user_formats[fmts++].code = code.code; + } else { + ret = ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + if (ret < 0) + goto egfmt; + fmts += ret; + } + + icd->num_user_formats = fmts; + icd->current_fmt = &icd->user_formats[0]; + + return 0; + +egfmt: + vfree(icd->user_formats); + return ret; +} + +/* Always entered with .host_lock held */ +static void soc_camera_free_user_formats(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->put_formats) + ici->ops->put_formats(icd); + icd->current_fmt = NULL; + icd->num_user_formats = 0; + vfree(icd->user_formats); + icd->user_formats = NULL; +} + +/* Called with .vb_lock held, or from the first open(2), see comment there */ +static int soc_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + + /* We always call try_fmt() before set_fmt() or set_selection() */ + ret = soc_camera_try_fmt(icd, f); + if (ret < 0) + return ret; + + ret = ici->ops->set_fmt(icd, f); + if (ret < 0) { + return ret; + } else if (!icd->current_fmt || + icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { + dev_err(icd->pdev, + "Host driver hasn't set up current format correctly!\n"); + return -EINVAL; + } + + icd->user_width = pix->width; + icd->user_height = pix->height; + icd->bytesperline = pix->bytesperline; + icd->sizeimage = pix->sizeimage; + icd->colorspace = pix->colorspace; + icd->field = pix->field; + + dev_dbg(icd->pdev, "set width: %d height: %d\n", + icd->user_width, icd->user_height); + + /* set physical bus parameters */ + return ici->ops->set_bus_param(icd); +} + +static int soc_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + if (ici->icd) + return -EBUSY; + + if (!icd->clk) { + ret = soc_camera_clock_start(ici); + if (ret < 0) + return ret; + } + + if (ici->ops->add) { + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + } + + ici->icd = icd; + + return 0; + +eadd: + if (!icd->clk) + soc_camera_clock_stop(ici); + return ret; +} + +static void soc_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (WARN_ON(icd != ici->icd)) + return; + + if (ici->ops->remove) + ici->ops->remove(icd); + if (!icd->clk) + soc_camera_clock_stop(ici); + ici->icd = NULL; +} + +static int soc_camera_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct soc_camera_device *icd; + struct soc_camera_host *ici; + int ret; + + /* + * Don't mess with the host during probe: wait until the loop in + * scan_add_host() completes. Also protect against a race with + * soc_camera_host_unregister(). + */ + if (mutex_lock_interruptible(&list_lock)) + return -ERESTARTSYS; + + if (!vdev || !video_is_registered(vdev)) { + mutex_unlock(&list_lock); + return -ENODEV; + } + + icd = video_get_drvdata(vdev); + ici = to_soc_camera_host(icd->parent); + + ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; + mutex_unlock(&list_lock); + + if (ret < 0) { + dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); + return ret; + } + + if (!to_soc_camera_control(icd)) { + /* No device driver attached */ + ret = -ENODEV; + goto econtrol; + } + + if (mutex_lock_interruptible(&ici->host_lock)) { + ret = -ERESTARTSYS; + goto elockhost; + } + icd->use_count++; + + /* Now we really have to activate the camera */ + if (icd->use_count == 1) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + /* Restore parameters before the last close() per V4L2 API */ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = icd->user_width, + .height = icd->user_height, + .field = icd->field, + .colorspace = icd->colorspace, + .pixelformat = + icd->current_fmt->host_fmt->fourcc, + }, + }; + + /* The camera could have been already on, try to reset */ + if (sdesc->subdev_desc.reset) + if (icd->control) + sdesc->subdev_desc.reset(icd->control); + + ret = soc_camera_add_device(icd); + if (ret < 0) { + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); + goto eiciadd; + } + + ret = __soc_camera_power_on(icd); + if (ret < 0) + goto epower; + + pm_runtime_enable(&icd->vdev->dev); + ret = pm_runtime_resume(&icd->vdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + + /* + * Try to configure with default parameters. Notice: this is the + * very first open, so, we cannot race against other calls, + * apart from someone else calling open() simultaneously, but + * .host_lock is protecting us against it. + */ + ret = soc_camera_set_fmt(icd, &f); + if (ret < 0) + goto esfmt; + + ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); + if (ret < 0) + goto einitvb; + v4l2_ctrl_handler_setup(&icd->ctrl_handler); + } + mutex_unlock(&ici->host_lock); + + file->private_data = icd; + dev_dbg(icd->pdev, "camera device open\n"); + + return 0; + + /* + * All errors are entered with the .host_lock held, first four also + * with use_count == 1 + */ +einitvb: +esfmt: + pm_runtime_disable(&icd->vdev->dev); +eresume: + __soc_camera_power_off(icd); +epower: + soc_camera_remove_device(icd); +eiciadd: + icd->use_count--; + mutex_unlock(&ici->host_lock); +elockhost: +econtrol: + module_put(ici->ops->owner); + + return ret; +} + +static int soc_camera_close(struct file *file) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + mutex_lock(&ici->host_lock); + if (icd->streamer == file) { + if (ici->ops->init_videobuf2) + vb2_queue_release(&icd->vb2_vidq); + icd->streamer = NULL; + } + icd->use_count--; + if (!icd->use_count) { + pm_runtime_suspend(&icd->vdev->dev); + pm_runtime_disable(&icd->vdev->dev); + + __soc_camera_power_off(icd); + + soc_camera_remove_device(icd); + } + + mutex_unlock(&ici->host_lock); + + module_put(ici->ops->owner); + + dev_dbg(icd->pdev, "camera device close\n"); + + return 0; +} + +static ssize_t soc_camera_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + dev_dbg(icd->pdev, "read called, buf %p\n", buf); + + if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) + return vb2_read(&icd->vb2_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + + dev_err(icd->pdev, "camera device read not implemented\n"); + + return -EINVAL; +} + +static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int err; + + dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); + + if (icd->streamer != file) + return -EBUSY; + + if (mutex_lock_interruptible(&ici->host_lock)) + return -ERESTARTSYS; + err = vb2_mmap(&icd->vb2_vidq, vma); + mutex_unlock(&ici->host_lock); + + dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, + err); + + return err; +} + +static __poll_t soc_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + __poll_t res = EPOLLERR; + + if (icd->streamer != file) + return EPOLLERR; + + mutex_lock(&ici->host_lock); + res = ici->ops->poll(file, pt); + mutex_unlock(&ici->host_lock); + return res; +} + +static const struct v4l2_file_operations soc_camera_fops = { + .owner = THIS_MODULE, + .open = soc_camera_open, + .release = soc_camera_close, + .unlocked_ioctl = video_ioctl2, + .read = soc_camera_read, + .mmap = soc_camera_mmap, + .poll = soc_camera_poll, +}; + +static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + int ret; + + WARN_ON(priv != file->private_data); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); + return -EINVAL; + } + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + if (vb2_is_streaming(&icd->vb2_vidq)) { + dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); + return -EBUSY; + } + + ret = soc_camera_set_fmt(icd, f); + + if (!ret && !icd->streamer) + icd->streamer = file; + + return ret; +} + +static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct soc_camera_device *icd = file->private_data; + const struct soc_mbus_pixelfmt *format; + + WARN_ON(priv != file->private_data); + + if (f->index >= icd->num_user_formats) + return -EINVAL; + + format = icd->user_formats[f->index].host_fmt; + + if (format->name) + strscpy(f->description, format->name, sizeof(f->description)); + f->pixelformat = format->fourcc; + return 0; +} + +static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_pix_format *pix = &f->fmt.pix; + + WARN_ON(priv != file->private_data); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pix->width = icd->user_width; + pix->height = icd->user_height; + pix->bytesperline = icd->bytesperline; + pix->sizeimage = icd->sizeimage; + pix->field = icd->field; + pix->pixelformat = icd->current_fmt->host_fmt->fourcc; + pix->colorspace = icd->colorspace; + dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", + icd->current_fmt->host_fmt->fourcc); + return 0; +} + +static int soc_camera_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + WARN_ON(priv != file->private_data); + + strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); + return ici->ops->querycap(ici, cap); +} + +static int soc_camera_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + WARN_ON(priv != file->private_data); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (icd->streamer != file) + return -EBUSY; + + /* This calls buf_queue from host driver's videobuf2_queue_ops */ + ret = vb2_streamon(&icd->vb2_vidq, i); + if (!ret) + v4l2_subdev_call(sd, video, s_stream, 1); + + return ret; +} + +static int soc_camera_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + WARN_ON(priv != file->private_data); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (icd->streamer != file) + return -EBUSY; + + /* + * This calls buf_release from host driver's videobuf2_queue_ops for all + * remaining buffers. When the last buffer is freed, stop capture + */ + ret = vb2_streamoff(&icd->vb2_vidq, i); + + v4l2_subdev_call(sd, video, s_stream, 0); + + return ret; +} + +static int soc_camera_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + /* With a wrong type no need to try to fall back to cropping */ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return ici->ops->get_selection(icd, s); +} + +static int soc_camera_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + /* In all these cases cropping emulation will not help */ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + (s->target != V4L2_SEL_TGT_COMPOSE && + s->target != V4L2_SEL_TGT_CROP)) + return -EINVAL; + + if (s->target == V4L2_SEL_TGT_COMPOSE) { + /* No output size change during a running capture! */ + if (vb2_is_streaming(&icd->vb2_vidq) && + (icd->user_width != s->r.width || + icd->user_height != s->r.height)) + return -EBUSY; + + /* + * Only one user is allowed to change the output format, touch + * buffers, start / stop streaming, poll for data + */ + if (icd->streamer && icd->streamer != file) + return -EBUSY; + } + + if (s->target == V4L2_SEL_TGT_CROP && + vb2_is_streaming(&icd->vb2_vidq) && + ici->ops->set_liveselection) + ret = ici->ops->set_liveselection(icd, s); + else + ret = ici->ops->set_selection(icd, s); + if (!ret && + s->target == V4L2_SEL_TGT_COMPOSE) { + icd->user_width = s->r.width; + icd->user_height = s->r.height; + if (!icd->streamer) + icd->streamer = file; + } + + return ret; +} + +static int soc_camera_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->get_parm) + return ici->ops->get_parm(icd, a); + + return -ENOIOCTLCMD; +} + +static int soc_camera_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->set_parm) + return ici->ops->set_parm(icd, a); + + return -ENOIOCTLCMD; +} + +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd); + +/* So far this function cannot fail */ +static void scan_add_host(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; + + /* The camera could have been already on, try to reset */ + if (ssdd->reset) + if (icd->control) + ssdd->reset(icd->control); + + icd->parent = ici->v4l2_dev.dev; + + /* Ignore errors */ + soc_camera_probe(ici, icd); + } + + mutex_unlock(&list_lock); +} + +/* + * It is invalid to call v4l2_clk_enable() after a successful probing + * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. + */ +static int soc_camera_clk_enable(struct v4l2_clk *clk) +{ + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + + if (!icd || !icd->parent) + return -ENODEV; + + ici = to_soc_camera_host(icd->parent); + + if (!try_module_get(ici->ops->owner)) + return -ENODEV; + + /* + * If a different client is currently being probed, the host will tell + * you to go + */ + return soc_camera_clock_start(ici); +} + +static void soc_camera_clk_disable(struct v4l2_clk *clk) +{ + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + + if (!icd || !icd->parent) + return; + + ici = to_soc_camera_host(icd->parent); + + soc_camera_clock_stop(ici); + + module_put(ici->ops->owner); +} + +/* + * Eventually, it would be more logical to make the respective host the clock + * owner, but then we would have to copy this struct for each ici. Besides, it + * would introduce the circular dependency problem, unless we port all client + * drivers to release the clock, when not in use. + */ +static const struct v4l2_clk_ops soc_camera_clk_ops = { + .owner = THIS_MODULE, + .enable = soc_camera_clk_enable, + .disable = soc_camera_clk_disable, +}; + +static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, + struct soc_camera_async_client *sasc) +{ + struct platform_device *pdev; + int ret, i; + + mutex_lock(&list_lock); + i = find_first_zero_bit(device_map, MAP_MAX_NUM); + if (i < MAP_MAX_NUM) + set_bit(i, device_map); + mutex_unlock(&list_lock); + if (i >= MAP_MAX_NUM) + return -ENOMEM; + + pdev = platform_device_alloc("soc-camera-pdrv", i); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); + if (ret < 0) { + platform_device_put(pdev); + return ret; + } + + sasc->pdev = pdev; + + return 0; +} + +static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) +{ + struct platform_device *pdev = sasc->pdev; + int ret; + + ret = platform_device_add(pdev); + if (ret < 0 || !pdev->dev.driver) + return NULL; + + return platform_get_drvdata(pdev); +} + +/* Locking: called with .host_lock held */ +static int soc_camera_probe_finish(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + sd->grp_id = soc_camera_grp_id(icd); + v4l2_set_subdev_hostdata(sd, icd); + + v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); + + ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, + NULL, true); + if (ret < 0) + return ret; + + ret = soc_camera_add_device(icd); + if (ret < 0) { + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); + return ret; + } + + /* At this point client .probe() should have run already */ + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eusrfmt; + + icd->field = V4L2_FIELD_ANY; + + ret = soc_camera_video_start(icd); + if (ret < 0) + goto evidstart; + + /* Try to improve our guess of a reasonable window format */ + if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { + icd->user_width = mf->width; + icd->user_height = mf->height; + icd->colorspace = mf->colorspace; + icd->field = mf->field; + } + soc_camera_remove_device(icd); + + return 0; + +evidstart: + soc_camera_free_user_formats(icd); +eusrfmt: + soc_camera_remove_device(icd); + + return ret; +} + +#ifdef CONFIG_I2C_BOARDINFO +static int soc_camera_i2c_init(struct soc_camera_device *icd, + struct soc_camera_desc *sdesc) +{ + struct soc_camera_subdev_desc *ssdd; + struct i2c_client *client; + struct soc_camera_host *ici; + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct i2c_adapter *adap; + struct v4l2_subdev *subdev; + char clk_name[V4L2_CLK_NAME_SIZE]; + int ret; + + /* First find out how we link the main client */ + if (icd->sasc) { + /* Async non-OF probing handled by the subdevice list */ + return -EPROBE_DEFER; + } + + ici = to_soc_camera_host(icd->parent); + adap = i2c_get_adapter(shd->i2c_adapter_id); + if (!adap) { + dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", + shd->i2c_adapter_id); + return -ENODEV; + } + + ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); + if (!ssdd) { + ret = -ENOMEM; + goto ealloc; + } + /* + * In synchronous case we request regulators ourselves in + * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try + * to allocate them again. + */ + ssdd->sd_pdata.num_regulators = 0; + ssdd->sd_pdata.regulators = NULL; + shd->board_info->platform_data = ssdd; + + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + shd->i2c_adapter_id, shd->board_info->addr); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, + shd->board_info, NULL); + if (!subdev) { + ret = -ENODEV; + goto ei2cnd; + } + + client = v4l2_get_subdevdata(subdev); + + /* Use to_i2c_client(dev) to recover the i2c client */ + icd->control = &client->dev; + + return 0; +ei2cnd: + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; +eclkreg: + kfree(ssdd); +ealloc: + i2c_put_adapter(adap); + return ret; +} + +static void soc_camera_i2c_free(struct soc_camera_device *icd) +{ + struct i2c_client *client = + to_i2c_client(to_soc_camera_control(icd)); + struct i2c_adapter *adap; + struct soc_camera_subdev_desc *ssdd; + + icd->control = NULL; + if (icd->sasc) + return; + + adap = client->adapter; + ssdd = client->dev.platform_data; + v4l2_device_unregister_subdev(i2c_get_clientdata(client)); + i2c_unregister_device(client); + i2c_put_adapter(adap); + kfree(ssdd); + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; +} + +/* + * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async + * internal global mutex, therefore cannot race against other asynchronous + * events. Until notifier->complete() (soc_camera_async_complete()) is called, + * the video device node is not registered and no V4L fops can occur. Unloading + * of the host driver also calls a v4l2-async function, so also there we're + * protected. + */ +static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (asd == sasc->sensor && !WARN_ON(icd->control)) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* + * Only now we get subdevice-specific information like + * regulators, flags, callbacks, etc. + */ + if (client) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = + soc_camera_i2c_to_desc(client); + if (ssdd) { + memcpy(&sdesc->subdev_desc, ssdd, + sizeof(sdesc->subdev_desc)); + if (ssdd->reset) + ssdd->reset(&client->dev); + } + + icd->control = &client->dev; + } + } + + return 0; +} + +static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + icd->control = NULL; + + if (icd->clk) { + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } +} + +static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (to_soc_camera_control(icd)) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + mutex_lock(&list_lock); + ret = soc_camera_probe(ici, icd); + mutex_unlock(&list_lock); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations soc_camera_async_ops = { + .bound = soc_camera_async_bound, + .unbind = soc_camera_async_unbind, + .complete = soc_camera_async_complete, +}; + +static int scan_async_group(struct soc_camera_host *ici, + struct v4l2_async_subdev **asd, unsigned int size) +{ + struct soc_camera_async_subdev *sasd; + struct soc_camera_async_client *sasc; + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + char clk_name[V4L2_CLK_NAME_SIZE]; + unsigned int i; + int ret; + + /* First look for a sensor */ + for (i = 0; i < size; i++) { + sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); + if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) + break; + } + + if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { + /* All useless */ + dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); + return -ENODEV; + } + + /* Or shall this be managed by the soc-camera device? */ + sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); + if (!sasc) + return -ENOMEM; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &sasd->asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + v4l2_async_notifier_init(&sasc->notifier); + + for (i = 0; i < size; i++) { + ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); + if (ret) + goto eaddasd; + } + + sasc->notifier.ops = &soc_camera_async_ops; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + sasd->asd.match.i2c.adapter_id, + sasd->asd.match.i2c.address); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; +eaddasd: + v4l2_async_notifier_cleanup(&sasc->notifier); + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, sasc); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_async_host(struct soc_camera_host *ici) +{ + struct v4l2_async_subdev **asd; + int j; + + for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { + scan_async_group(ici, asd, ici->asd_sizes[j]); + asd += ici->asd_sizes[j]; + } +} +#else +#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) +#define soc_camera_i2c_free(icd) do {} while (0) +#define scan_async_host(ici) do {} while (0) +#endif + +#ifdef CONFIG_OF + +struct soc_of_info { + struct soc_camera_async_subdev sasd; + struct soc_camera_async_client sasc; + struct v4l2_async_subdev *subdev; +}; + +static int soc_of_bind(struct soc_camera_host *ici, + struct device_node *ep, + struct device_node *remote) +{ + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + struct soc_camera_async_client *sasc; + struct soc_of_info *info; + struct i2c_client *client; + char clk_name[V4L2_CLK_NAME_SIZE]; + int ret; + + /* allocate a new subdev and add match info to it */ + info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->sasd.asd.match.fwnode = of_fwnode_handle(remote); + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + info->subdev = &info->sasd.asd; + + /* Or shall this be managed by the soc-camera device? */ + sasc = &info->sasc; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &info->sasd.asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + v4l2_async_notifier_init(&sasc->notifier); + + ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); + if (ret) { + of_node_put(remote); + goto eaddasd; + } + + sasc->notifier.ops = &soc_camera_async_ops; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + client = of_find_i2c_device_by_node(remote); + + if (client) + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + client->adapter->nr, client->addr); + else + v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; +eaddasd: + v4l2_async_notifier_cleanup(&sasc->notifier); + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, info); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_of_host(struct soc_camera_host *ici) +{ + struct device *dev = ici->v4l2_dev.dev; + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *rem; + unsigned int i; + + for (i = 0; ; i++) { + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + break; + + rem = of_graph_get_remote_port_parent(epn); + if (!rem) { + dev_notice(dev, "no remote for %pOF\n", epn); + continue; + } + + /* so we now have a remote node to connect */ + if (!i) + soc_of_bind(ici, epn, rem); + + if (i) { + dev_err(dev, "multiple subdevices aren't supported yet!\n"); + break; + } + } + + of_node_put(epn); +} + +#else +static inline void scan_of_host(struct soc_camera_host *ici) { } +#endif + +/* Called during host-driver probe */ +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd) +{ + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct device *control = NULL; + int ret; + + dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); + + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); + if (ret < 0) + return ret; + + /* Must have icd->vdev before registering the device */ + ret = video_dev_create(icd); + if (ret < 0) + goto evdc; + + /* + * ..._video_start() will create a device node, video_register_device() + * itself is protected against concurrent open() calls, but we also have + * to protect our data also during client probing. + */ + + /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ + if (shd->board_info) { + ret = soc_camera_i2c_init(icd, sdesc); + if (ret < 0 && ret != -EPROBE_DEFER) + goto eadd; + } else if (!shd->add_device || !shd->del_device) { + ret = -EINVAL; + goto eadd; + } else { + ret = soc_camera_clock_start(ici); + if (ret < 0) + goto eadd; + + if (shd->module_name) + ret = request_module(shd->module_name); + + ret = shd->add_device(icd); + if (ret < 0) + goto eadddev; + + /* + * FIXME: this is racy, have to use driver-binding notification, + * when it is available + */ + control = to_soc_camera_control(icd); + if (!control || !control->driver || !dev_get_drvdata(control) || + !try_module_get(control->driver->owner)) { + shd->del_device(icd); + ret = -ENODEV; + goto enodrv; + } + } + + mutex_lock(&ici->host_lock); + ret = soc_camera_probe_finish(icd); + mutex_unlock(&ici->host_lock); + if (ret < 0) + goto efinish; + + return 0; + +efinish: + if (shd->board_info) { + soc_camera_i2c_free(icd); + } else { + shd->del_device(icd); + module_put(control->driver->owner); +enodrv: +eadddev: + soc_camera_clock_stop(ici); + } +eadd: + if (icd->vdev) { + video_device_release(icd->vdev); + icd->vdev = NULL; + } +evdc: + v4l2_ctrl_handler_free(&icd->ctrl_handler); + return ret; +} + +/* + * This is called on device_unregister, which only means we have to disconnect + * from the host, but not remove ourselves from the device list. With + * asynchronous client probing this can also be called without + * soc_camera_probe_finish() having run. Careful with clean up. + */ +static int soc_camera_remove(struct soc_camera_device *icd) +{ + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct video_device *vdev = icd->vdev; + + v4l2_ctrl_handler_free(&icd->ctrl_handler); + if (vdev) { + video_unregister_device(vdev); + icd->vdev = NULL; + } + + if (sdesc->host_desc.board_info) { + soc_camera_i2c_free(icd); + } else { + struct device *dev = to_soc_camera_control(icd); + struct device_driver *drv = dev ? dev->driver : NULL; + if (drv) { + sdesc->host_desc.del_device(icd); + module_put(drv->owner); + } + } + + if (icd->num_user_formats) + soc_camera_free_user_formats(icd); + + if (icd->clk) { + /* For the synchronous case */ + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } + + if (icd->sasc) + platform_device_unregister(icd->sasc->pdev); + + return 0; +} + +static int default_g_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; +} + +static int default_s_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; +} + +static int default_g_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_g_parm_cap(icd->vdev, sd, a); +} + +static int default_s_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_s_parm_cap(icd->vdev, sd, a); +} + +static int default_enum_framesizes(struct soc_camera_device *icd, + struct v4l2_frmsizeenum *fsize) +{ + int ret; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); + if (!xlate) + return -EINVAL; + fse.code = xlate->code; + + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); + if (ret < 0) + return ret; + + if (fse.min_width == fse.max_width && + fse.min_height == fse.max_height) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.min_width; + fsize->discrete.height = fse.min_height; + return 0; + } + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = fse.min_width; + fsize->stepwise.max_width = fse.max_width; + fsize->stepwise.min_height = fse.min_height; + fsize->stepwise.max_height = fse.max_height; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + return 0; +} + +int soc_camera_host_register(struct soc_camera_host *ici) +{ + struct soc_camera_host *ix; + int ret; + + if (!ici || !ici->ops || + !ici->ops->try_fmt || + !ici->ops->set_fmt || + !ici->ops->set_bus_param || + !ici->ops->querycap || + !ici->ops->init_videobuf2 || + !ici->ops->poll || + !ici->v4l2_dev.dev) + return -EINVAL; + + if (!ici->ops->set_selection) + ici->ops->set_selection = default_s_selection; + if (!ici->ops->get_selection) + ici->ops->get_selection = default_g_selection; + if (!ici->ops->set_parm) + ici->ops->set_parm = default_s_parm; + if (!ici->ops->get_parm) + ici->ops->get_parm = default_g_parm; + if (!ici->ops->enum_framesizes) + ici->ops->enum_framesizes = default_enum_framesizes; + + mutex_lock(&list_lock); + list_for_each_entry(ix, &hosts, list) { + if (ix->nr == ici->nr) { + ret = -EBUSY; + goto edevreg; + } + } + + ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); + if (ret < 0) + goto edevreg; + + list_add_tail(&ici->list, &hosts); + mutex_unlock(&list_lock); + + mutex_init(&ici->host_lock); + mutex_init(&ici->clk_lock); + + if (ici->v4l2_dev.dev->of_node) + scan_of_host(ici); + else if (ici->asd_sizes) + /* + * No OF, host with a list of subdevices. Don't try to mix + * modes by initialising some groups statically and some + * dynamically! + */ + scan_async_host(ici); + else + /* Legacy: static platform devices from board data */ + scan_add_host(ici); + + return 0; + +edevreg: + mutex_unlock(&list_lock); + return ret; +} +EXPORT_SYMBOL(soc_camera_host_register); + +/* Unregister all clients! */ +void soc_camera_host_unregister(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd, *tmp; + struct soc_camera_async_client *sasc; + LIST_HEAD(notifiers); + + mutex_lock(&list_lock); + list_del(&ici->list); + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr && icd->sasc) { + /* as long as we hold the device, sasc won't be freed */ + get_device(icd->pdev); + list_add(&icd->sasc->list, ¬ifiers); + } + mutex_unlock(&list_lock); + + list_for_each_entry(sasc, ¬ifiers, list) { + /* Must call unlocked to avoid AB-BA dead-lock */ + v4l2_async_notifier_unregister(&sasc->notifier); + v4l2_async_notifier_cleanup(&sasc->notifier); + put_device(&sasc->pdev->dev); + } + + mutex_lock(&list_lock); + + list_for_each_entry_safe(icd, tmp, &devices, list) + if (icd->iface == ici->nr) + soc_camera_remove(icd); + + mutex_unlock(&list_lock); + + v4l2_device_unregister(&ici->v4l2_dev); +} +EXPORT_SYMBOL(soc_camera_host_unregister); + +/* Image capture device */ +static int soc_camera_device_register(struct soc_camera_device *icd) +{ + struct soc_camera_device *ix; + int num = -1, i; + + mutex_lock(&list_lock); + for (i = 0; i < 256 && num < 0; i++) { + num = i; + /* Check if this index is available on this interface */ + list_for_each_entry(ix, &devices, list) { + if (ix->iface == icd->iface && ix->devnum == i) { + num = -1; + break; + } + } + } + + if (num < 0) { + /* + * ok, we have 256 cameras on this host... + * man, stay reasonable... + */ + mutex_unlock(&list_lock); + return -ENOMEM; + } + + icd->devnum = num; + icd->use_count = 0; + icd->host_priv = NULL; + + /* + * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting + * it again + */ + i = to_platform_device(icd->pdev)->id; + if (i < 0) + /* One static (legacy) soc-camera platform device */ + i = 0; + if (i >= MAP_MAX_NUM) { + mutex_unlock(&list_lock); + return -EBUSY; + } + set_bit(i, device_map); + list_add_tail(&icd->list, &devices); + mutex_unlock(&list_lock); + + return 0; +} + +static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { + .vidioc_querycap = soc_camera_querycap, + .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, + .vidioc_enum_input = soc_camera_enum_input, + .vidioc_g_input = soc_camera_g_input, + .vidioc_s_input = soc_camera_s_input, + .vidioc_s_std = soc_camera_s_std, + .vidioc_g_std = soc_camera_g_std, + .vidioc_enum_framesizes = soc_camera_enum_framesizes, + .vidioc_reqbufs = soc_camera_reqbufs, + .vidioc_querybuf = soc_camera_querybuf, + .vidioc_qbuf = soc_camera_qbuf, + .vidioc_dqbuf = soc_camera_dqbuf, + .vidioc_create_bufs = soc_camera_create_bufs, + .vidioc_prepare_buf = soc_camera_prepare_buf, + .vidioc_expbuf = soc_camera_expbuf, + .vidioc_streamon = soc_camera_streamon, + .vidioc_streamoff = soc_camera_streamoff, + .vidioc_g_selection = soc_camera_g_selection, + .vidioc_s_selection = soc_camera_s_selection, + .vidioc_g_parm = soc_camera_g_parm, + .vidioc_s_parm = soc_camera_s_parm, +}; + +static int video_dev_create(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct video_device *vdev = video_device_alloc(); + + if (!vdev) + return -ENOMEM; + + strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); + + vdev->v4l2_dev = &ici->v4l2_dev; + vdev->fops = &soc_camera_fops; + vdev->ioctl_ops = &soc_camera_ioctl_ops; + vdev->release = video_device_release; + vdev->ctrl_handler = &icd->ctrl_handler; + vdev->lock = &ici->host_lock; + + icd->vdev = vdev; + + return 0; +} + +/* + * Called from soc_camera_probe() above with .host_lock held + */ +static int soc_camera_video_start(struct soc_camera_device *icd) +{ + const struct device_type *type = icd->vdev->dev.type; + int ret; + + if (!icd->parent) + return -ENODEV; + + video_set_drvdata(icd->vdev, icd); + if (icd->vdev->tvnorms == 0) { + /* disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); + } + ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(icd->pdev, "video_register_device failed: %d\n", ret); + return ret; + } + + /* Restore device type, possibly set by the subdevice driver */ + icd->vdev->dev.type = type; + + return 0; +} + +static int soc_camera_pdrv_probe(struct platform_device *pdev) +{ + struct soc_camera_desc *sdesc = pdev->dev.platform_data; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; + struct soc_camera_device *icd; + int ret; + + if (!sdesc) + return -EINVAL; + + icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); + if (!icd) + return -ENOMEM; + + /* + * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below + * regulator allocation is a dummy. They are actually requested by the + * subdevice driver, using soc_camera_power_init(). Also note, that in + * that case regulators are attached to the I2C device and not to the + * camera platform device. + */ + ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (ret < 0) + return ret; + + icd->iface = sdesc->host_desc.bus_id; + icd->sdesc = sdesc; + icd->pdev = &pdev->dev; + platform_set_drvdata(pdev, icd); + + icd->user_width = DEFAULT_WIDTH; + icd->user_height = DEFAULT_HEIGHT; + + return soc_camera_device_register(icd); +} + +/* + * Only called on rmmod for each platform device, since they are not + * hot-pluggable. Now we know, that all our users - hosts and devices have + * been unloaded already + */ +static int soc_camera_pdrv_remove(struct platform_device *pdev) +{ + struct soc_camera_device *icd = platform_get_drvdata(pdev); + int i; + + if (!icd) + return -EINVAL; + + i = pdev->id; + if (i < 0) + i = 0; + + /* + * In synchronous mode with static platform devices this is called in a + * loop from drivers/base/dd.c::driver_detach(), no parallel execution, + * no need to lock. In asynchronous case the caller - + * soc_camera_host_unregister() - already holds the lock + */ + if (test_bit(i, device_map)) { + clear_bit(i, device_map); + list_del(&icd->list); + } + + return 0; +} + +static struct platform_driver __refdata soc_camera_pdrv = { + .probe = soc_camera_pdrv_probe, + .remove = soc_camera_pdrv_remove, + .driver = { + .name = "soc-camera-pdrv", + }, +}; + +module_platform_driver(soc_camera_pdrv); + +MODULE_DESCRIPTION("Image capture bus driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/staging/media/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c new file mode 100644 index 000000000000..be74008ec0ca --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_mediabus.c @@ -0,0 +1,533 @@ +/* + * soc-camera media bus helper routines + * + * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/drv-intf/soc_mediabus.h> + +static const struct soc_mbus_lookup mbus_fmt[] = { +{ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555, + .name = "RGB555", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "RGB555X", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565X, + .name = "RGB565X", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB666_1X18, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB666/32bpp", + .bits_per_sample = 18, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 24, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_BE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .name = "Bayer 8 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_GREY, + .name = "Grey", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_Y10, + .name = "Grey 10bit", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADLO, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADLO, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_JPEG_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_JPEG, + .name = "JPEG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_VARIABLE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB444, + .name = "RGB444", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUV420, + .name = "YUYV 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVU420, + .name = "YVYU 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, + .name = "Bayer 10 BGGR DPCM 8", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .name = "Bayer 10 GBRG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .name = "Bayer 10 GRBG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .name = "Bayer 10 RGGB", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .name = "Bayer 12 BGGR", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .name = "Bayer 12 GBRG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .name = "Bayer 12 GRBG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .name = "Bayer 12 RGGB", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, +}; + +int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, + unsigned int *numerator, unsigned int *denominator) +{ + switch (mf->packing) { + case SOC_MBUS_PACKING_NONE: + case SOC_MBUS_PACKING_EXTEND16: + *numerator = 1; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_EXTEND32: + *numerator = 1; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + *numerator = 2; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_1_5X8: + *numerator = 3; + *denominator = 2; + return 0; + case SOC_MBUS_PACKING_VARIABLE: + *numerator = 0; + *denominator = 1; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL(soc_mbus_samples_per_pixel); + +s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) +{ + if (mf->layout != SOC_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + + switch (mf->packing) { + case SOC_MBUS_PACKING_NONE: + return width * mf->bits_per_sample / 8; + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + case SOC_MBUS_PACKING_EXTEND16: + return width * 2; + case SOC_MBUS_PACKING_1_5X8: + return width * 3 / 2; + case SOC_MBUS_PACKING_VARIABLE: + return 0; + case SOC_MBUS_PACKING_EXTEND32: + return width * 4; + } + return -EINVAL; +} +EXPORT_SYMBOL(soc_mbus_bytes_per_line); + +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + if (mf->layout == SOC_MBUS_LAYOUT_PACKED) + return bytes_per_line * height; + + switch (mf->packing) { + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + return bytes_per_line * height * 2; + case SOC_MBUS_PACKING_1_5X8: + return bytes_per_line * height * 3 / 2; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(soc_mbus_image_size); + +const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( + u32 code, + const struct soc_mbus_lookup *lookup, + int n) +{ + int i; + + for (i = 0; i < n; i++) + if (lookup[i].code == code) + return &lookup[i].fmt; + + return NULL; +} +EXPORT_SYMBOL(soc_mbus_find_fmtdesc); + +const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( + u32 code) +{ + return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); +} +EXPORT_SYMBOL(soc_mbus_get_fmtdesc); + +unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags) +{ + unsigned long common_flags; + bool hsync = true, vsync = true, pclk, data, mode; + bool mipi_lanes, mipi_clock; + + common_flags = cfg->flags & flags; + + switch (cfg->type) { + case V4L2_MBUS_PARALLEL: + hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW); + vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW); + /* fall through */ + case V4L2_MBUS_BT656: + pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING); + data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); + mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); + return (!hsync || !vsync || !pclk || !data || !mode) ? + 0 : common_flags; + case V4L2_MBUS_CSI2_DPHY: + mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; + mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); + return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; + default: + WARN_ON(1); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(soc_mbus_config_compatible); + +static int __init soc_mbus_init(void) +{ + return 0; +} + +static void __exit soc_mbus_exit(void) +{ +} + +module_init(soc_mbus_init); +module_exit(soc_mbus_exit); + +MODULE_DESCRIPTION("soc-camera media bus interface"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c new file mode 100644 index 000000000000..6d922b17ea94 --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_mt9v022.c @@ -0,0 +1,1012 @@ +/* + * Driver for MT9V022 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski <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 version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/log2.h> +#include <linux/module.h> + +#include <media/i2c/mt9v022.h> +#include <media/soc_camera.h> +#include <media/drv-intf/soc_mediabus.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-clk.h> +#include <media/v4l2-ctrls.h> + +/* + * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc + */ + +static char *sensor_type; +module_param(sensor_type, charp, S_IRUGO); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); + +/* mt9v022 selected register addresses */ +#define MT9V022_CHIP_VERSION 0x00 +#define MT9V022_COLUMN_START 0x01 +#define MT9V022_ROW_START 0x02 +#define MT9V022_WINDOW_HEIGHT 0x03 +#define MT9V022_WINDOW_WIDTH 0x04 +#define MT9V022_HORIZONTAL_BLANKING 0x05 +#define MT9V022_VERTICAL_BLANKING 0x06 +#define MT9V022_CHIP_CONTROL 0x07 +#define MT9V022_SHUTTER_WIDTH1 0x08 +#define MT9V022_SHUTTER_WIDTH2 0x09 +#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a +#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V022_RESET 0x0c +#define MT9V022_READ_MODE 0x0d +#define MT9V022_MONITOR_MODE 0x0e +#define MT9V022_PIXEL_OPERATION_MODE 0x0f +#define MT9V022_LED_OUT_CONTROL 0x1b +#define MT9V022_ADC_MODE_CONTROL 0x1c +#define MT9V022_REG32 0x20 +#define MT9V022_ANALOG_GAIN 0x35 +#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 +#define MT9V022_PIXCLK_FV_LV 0x74 +#define MT9V022_DIGITAL_TEST_PATTERN 0x7f +#define MT9V022_AEC_AGC_ENABLE 0xAF +#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD + +/* mt9v024 partial list register addresses changes with respect to mt9v022 */ +#define MT9V024_PIXCLK_FV_LV 0x72 +#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD + +/* Progressive scan, master, defaults */ +#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 + +#define MT9V022_MAX_WIDTH 752 +#define MT9V022_MAX_HEIGHT 480 +#define MT9V022_MIN_WIDTH 48 +#define MT9V022_MIN_HEIGHT 32 +#define MT9V022_COLUMN_SKIP 1 +#define MT9V022_ROW_SKIP 4 + +#define MT9V022_HORIZONTAL_BLANKING_MIN 43 +#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 +#define MT9V022_HORIZONTAL_BLANKING_DEF 94 +#define MT9V022_VERTICAL_BLANKING_MIN 2 +#define MT9V022_VERTICAL_BLANKING_MAX 3000 +#define MT9V022_VERTICAL_BLANKING_DEF 45 + +#define is_mt9v022_rev3(id) (id == 0x1313) +#define is_mt9v024(id) (id == 0x1324) + +/* MT9V022 has only one fixed colorspace per pixelcode */ +struct mt9v022_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct mt9v022_datafmt *mt9v022_find_datafmt( + u32 code, const struct mt9v022_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { + /* + * Order important: first natively supported, + * second supported with a GPIO extender + */ + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, +}; + +static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { + /* Order important - see above */ + {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, +}; + +/* only registers with different addresses on different mt9v02x sensors */ +struct mt9v02x_register { + u8 max_total_shutter_width; + u8 pixclk_fv_lv; +}; + +static const struct mt9v02x_register mt9v022_register = { + .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, + .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, +}; + +static const struct mt9v02x_register mt9v024_register = { + .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, + .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, +}; + +enum mt9v022_model { + MT9V022IX7ATM, + MT9V022IX7ATC, +}; + +struct mt9v022 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* gain/auto-gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_rect rect; /* Sensor window */ + struct v4l2_clk *clk; + const struct mt9v022_datafmt *fmt; + const struct mt9v022_datafmt *fmts; + const struct mt9v02x_register *reg; + int num_fmts; + enum mt9v022_model model; + u16 chip_control; + u16 chip_version; + unsigned short y_skip_top; /* Lines to skip at the top */ +}; + +static struct mt9v022 *to_mt9v022(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); +} + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret | data); +} + +static int reg_clear(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret & ~data); +} + +static int mt9v022_init(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + int ret; + + /* + * Almost the default mode: master, parallel, simultaneous, and an + * undocumented bit 0x200, which is present in table 7, but not in 8, + * plus snapshot mode to disable scan for now + */ + mt9v022->chip_control |= 0x10; + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (!ret) + ret = reg_write(client, MT9V022_READ_MODE, 0x300); + + /* All defaults */ + if (!ret) + /* AEC, AGC on */ + ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); + if (!ret) + ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); + if (!ret) + ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); + if (!ret) + ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); + if (!ret) + /* default - auto */ + ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); + if (!ret) + ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); + if (!ret) + return v4l2_ctrl_handler_setup(&mt9v022->hdl); + + return ret; +} + +static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (enable) { + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + if (is_mt9v022_rev3(mt9v022->chip_version) || + is_mt9v024(mt9v022->chip_version)) { + /* + * Unset snapshot mode specific settings: clear bit 9 + * and bit 2 in reg. 0x20 when in normal mode. + */ + if (reg_clear(client, MT9V022_REG32, 0x204)) + return -EIO; + } + } else { + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; + if (is_mt9v022_rev3(mt9v022->chip_version) || + is_mt9v024(mt9v022->chip_version)) { + /* + * Required settings for snapshot mode: set bit 9 + * (RST enable) and bit 2 (CR enable) in reg. 0x20 + * See TechNote TN0960 or TN-09-225. + */ + if (reg_set(client, MT9V022_REG32, 0x204)) + return -EIO; + } + } + + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int mt9v022_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_rect rect = sel->r; + int min_row, min_blank; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* Bayer format - even size lengths */ + if (mt9v022->fmts == mt9v022_colour_fmts) { + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + /* Let the user play with the starting pixel */ + } + + soc_camera_limit_side(&rect.left, &rect.width, + MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); + + /* Like in example app. Contradicts the datasheet though */ + ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); + if (ret >= 0) { + if (ret & 1) /* Autoexposure */ + ret = reg_write(client, mt9v022->reg->max_total_shutter_width, + rect.height + mt9v022->y_skip_top + 43); + /* + * If autoexposure is off, there is no need to set + * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off + * only if the user has set exposure manually, using the + * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. + * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH + * already contains the correct value. + */ + } + /* Setup frame format: defaults apart from width and height */ + if (!ret) + ret = reg_write(client, MT9V022_COLUMN_START, rect.left); + if (!ret) + ret = reg_write(client, MT9V022_ROW_START, rect.top); + /* + * mt9v022: min total row time is 660 columns, min blanking is 43 + * mt9v024: min total row time is 690 columns, min blanking is 61 + */ + if (is_mt9v024(mt9v022->chip_version)) { + min_row = 690; + min_blank = 61; + } else { + min_row = 660; + min_blank = 43; + } + if (!ret) + ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, + rect.width > min_row - min_blank ? + min_blank : min_row - rect.width); + if (!ret) + ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); + if (!ret) + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); + if (!ret) + ret = reg_write(client, MT9V022_WINDOW_HEIGHT, + rect.height + mt9v022->y_skip_top); + + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); + + mt9v022->rect = rect; + + return 0; +} + +static int mt9v022_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9V022_COLUMN_SKIP; + sel->r.top = MT9V022_ROW_SKIP; + sel->r.width = MT9V022_MAX_WIDTH; + sel->r.height = MT9V022_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9v022->rect; + return 0; + default: + return -EINVAL; + } +} + +static int mt9v022_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (format->pad) + return -EINVAL; + + mf->width = mt9v022->rect.width; + mf->height = mt9v022->rect.height; + mf->code = mt9v022->fmt->code; + mf->colorspace = mt9v022->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int mt9v022_s_fmt(struct v4l2_subdev *sd, + const struct mt9v022_datafmt *fmt, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9v022->rect.left, + .r.top = mt9v022->rect.top, + .r.width = mf->width, + .r.height = mf->height, + }; + int ret; + + /* + * The caller provides a supported format, as verified per call to + * .set_fmt(FORMAT_TRY), datawidth is from our supported format list + */ + switch (mf->code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_Y10_1X10: + if (mt9v022->model != MT9V022IX7ATM) + return -EINVAL; + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (mt9v022->model != MT9V022IX7ATC) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* No support for scaling on this camera, just crop. */ + ret = mt9v022_set_selection(sd, NULL, &sel); + if (!ret) { + mf->width = mt9v022->rect.width; + mf->height = mt9v022->rect.height; + mt9v022->fmt = fmt; + mf->colorspace = fmt->colorspace; + } + + return ret; +} + +static int mt9v022_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + const struct mt9v022_datafmt *fmt; + int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || + mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; + + if (format->pad) + return -EINVAL; + + v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, + MT9V022_MAX_WIDTH, align, + &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, + MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); + + fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, + mt9v022->num_fmts); + if (!fmt) { + fmt = mt9v022->fmt; + mf->code = fmt->code; + } + + mf->colorspace = fmt->colorspace; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9v022_s_fmt(sd, fmt, mf); + cfg->try_fmt = *mf; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v022_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 2; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9v022_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int mt9v022_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); +} + +static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl *gain = mt9v022->gain; + struct v4l2_ctrl *exp = mt9v022->exposure; + unsigned long range; + int data; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + data = reg_read(client, MT9V022_ANALOG_GAIN); + if (data < 0) + return -EIO; + + range = gain->maximum - gain->minimum; + gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; + return 0; + case V4L2_CID_EXPOSURE_AUTO: + data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); + if (data < 0) + return -EIO; + + range = exp->maximum - exp->minimum; + exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; + return 0; + case V4L2_CID_HBLANK: + data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); + if (data < 0) + return -EIO; + ctrl->val = data; + return 0; + case V4L2_CID_VBLANK: + data = reg_read(client, MT9V022_VERTICAL_BLANKING); + if (data < 0) + return -EIO; + ctrl->val = data; + return 0; + } + return -EINVAL; +} + +static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, MT9V022_READ_MODE, 0x10); + else + data = reg_clear(client, MT9V022_READ_MODE, 0x10); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_HFLIP: + if (ctrl->val) + data = reg_set(client, MT9V022_READ_MODE, 0x20); + else + data = reg_clear(client, MT9V022_READ_MODE, 0x20); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_AUTOGAIN: + if (ctrl->val) { + if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + } else { + struct v4l2_ctrl *gain = mt9v022->gain; + /* mt9v022 has minimum == default */ + unsigned long range = gain->maximum - gain->minimum; + /* Valid values 16 to 64, 32 to 64 must be even. */ + unsigned long gain_val = ((gain->val - (s32)gain->minimum) * + 48 + range / 2) / range + 16; + + if (gain_val >= 32) + gain_val &= ~1; + + /* + * The user wants to set gain manually, hope, she + * knows, what she's doing... Switch AGC off. + */ + if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + + dev_dbg(&client->dev, "Setting gain from %d to %lu\n", + reg_read(client, MT9V022_ANALOG_GAIN), gain_val); + if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) + return -EIO; + } + return 0; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_AUTO) { + data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); + } else { + struct v4l2_ctrl *exp = mt9v022->exposure; + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - (s32)exp->minimum) * + 479 + range / 2) / range + 1; + + /* + * The user wants to set shutter width manually, hope, + * she knows, what she's doing... Switch AEC off. + */ + data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); + if (data < 0) + return -EIO; + dev_dbg(&client->dev, "Shutter width from %d to %lu\n", + reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), + shutter); + if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, + shutter) < 0) + return -EIO; + } + return 0; + case V4L2_CID_HBLANK: + if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, + ctrl->val) < 0) + return -EIO; + return 0; + case V4L2_CID_VBLANK: + if (reg_write(client, MT9V022_VERTICAL_BLANKING, + ctrl->val) < 0) + return -EIO; + return 0; + } + return -EINVAL; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9v022_video_probe(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + s32 data; + int ret; + unsigned long flags; + + ret = mt9v022_s_power(&mt9v022->subdev, 1); + if (ret < 0) + return ret; + + /* Read out the chip version register */ + data = reg_read(client, MT9V022_CHIP_VERSION); + + /* must be 0x1311, 0x1313 or 0x1324 */ + if (data != 0x1311 && data != 0x1313 && data != 0x1324) { + ret = -ENODEV; + dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", + data); + goto ei2c; + } + + mt9v022->chip_version = data; + + mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : + &mt9v022_register; + + /* Soft reset */ + ret = reg_write(client, MT9V022_RESET, 1); + if (ret < 0) + goto ei2c; + /* 15 clock cycles */ + udelay(200); + if (reg_read(client, MT9V022_RESET)) { + dev_err(&client->dev, "Resetting MT9V022 failed!\n"); + if (ret > 0) + ret = -EIO; + goto ei2c; + } + + /* Set monochrome or colour sensor type */ + if (sensor_type && (!strcmp("colour", sensor_type) || + !strcmp("color", sensor_type))) { + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); + mt9v022->model = MT9V022IX7ATC; + mt9v022->fmts = mt9v022_colour_fmts; + } else { + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); + mt9v022->model = MT9V022IX7ATM; + mt9v022->fmts = mt9v022_monochrome_fmts; + } + + if (ret < 0) + goto ei2c; + + mt9v022->num_fmts = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + mt9v022->num_fmts++; + else + mt9v022->fmts++; + + if (flags & SOCAM_DATAWIDTH_8) + mt9v022->num_fmts++; + + mt9v022->fmt = &mt9v022->fmts[0]; + + dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + data, mt9v022->model == MT9V022IX7ATM ? + "monochrome" : "colour"); + + ret = mt9v022_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); + +ei2c: + mt9v022_s_power(&mt9v022->subdev, 0); + return ret; +} + +static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + *lines = mt9v022->y_skip_top; + + return 0; +} + +static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { + .g_volatile_ctrl = mt9v022_g_volatile_ctrl, + .s_ctrl = mt9v022_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v022_g_register, + .s_register = mt9v022_s_register, +#endif + .s_power = mt9v022_s_power, +}; + +static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (code->pad || code->index >= mt9v022->num_fmts) + return -EINVAL; + + code->code = mt9v022->fmts[code->index].code; + return 0; +} + +static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); + unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; + int ret; + u16 pixclk = 0; + + if (ssdd->set_bus_param) { + ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); + if (ret) + return ret; + } else if (bps != 10) { + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return -EINVAL; + } + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + pixclk |= 0x10; + + if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); + if (ret < 0) + return ret; + + if (!(flags & V4L2_MBUS_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + +static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { + .s_stream = mt9v022_s_stream, + .g_mbus_config = mt9v022_g_mbus_config, + .s_mbus_config = mt9v022_s_mbus_config, +}; + +static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { + .g_skip_top_lines = mt9v022_g_skip_top_lines, +}; + +static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { + .enum_mbus_code = mt9v022_enum_mbus_code, + .get_selection = mt9v022_get_selection, + .set_selection = mt9v022_set_selection, + .get_fmt = mt9v022_get_fmt, + .set_fmt = mt9v022_set_fmt, +}; + +static const struct v4l2_subdev_ops mt9v022_subdev_ops = { + .core = &mt9v022_subdev_core_ops, + .video = &mt9v022_subdev_video_ops, + .sensor = &mt9v022_subdev_sensor_ops, + .pad = &mt9v022_subdev_pad_ops, +}; + +static int mt9v022_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9v022 *mt9v022; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mt9v022_platform_data *pdata; + int ret; + + if (!ssdd) { + dev_err(&client->dev, "MT9V022 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); + if (!mt9v022) + return -ENOMEM; + + pdata = ssdd->drv_priv; + v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); + v4l2_ctrl_handler_init(&mt9v022->hdl, 6); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, + &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + + mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, + MT9V022_HORIZONTAL_BLANKING_MAX, 1, + MT9V022_HORIZONTAL_BLANKING_DEF); + + mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, + MT9V022_VERTICAL_BLANKING_MAX, 1, + MT9V022_VERTICAL_BLANKING_DEF); + + mt9v022->subdev.ctrl_handler = &mt9v022->hdl; + if (mt9v022->hdl.error) { + int err = mt9v022->hdl.error; + + dev_err(&client->dev, "control initialisation err %d\n", err); + return err; + } + v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); + + mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; + + /* + * On some platforms the first read out line is corrupted. + * Workaround it by skipping if indicated by platform data. + */ + mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; + mt9v022->rect.left = MT9V022_COLUMN_SKIP; + mt9v022->rect.top = MT9V022_ROW_SKIP; + mt9v022->rect.width = MT9V022_MAX_WIDTH; + mt9v022->rect.height = MT9V022_MAX_HEIGHT; + + mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9v022->clk)) { + ret = PTR_ERR(mt9v022->clk); + goto eclkget; + } + + ret = mt9v022_video_probe(client); + if (ret) { + v4l2_clk_put(mt9v022->clk); +eclkget: + v4l2_ctrl_handler_free(&mt9v022->hdl); + } + + return ret; +} + +static int mt9v022_remove(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + v4l2_clk_put(mt9v022->clk); + v4l2_device_unregister_subdev(&mt9v022->subdev); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + v4l2_ctrl_handler_free(&mt9v022->hdl); + + return 0; +} +static const struct i2c_device_id mt9v022_id[] = { + { "mt9v022", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v022_id); + +static struct i2c_driver mt9v022_i2c_driver = { + .driver = { + .name = "mt9v022", + }, + .probe = mt9v022_probe, + .remove = mt9v022_remove, + .id_table = mt9v022_id, +}; + +module_i2c_driver(mt9v022_i2c_driver); + +MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c new file mode 100644 index 000000000000..0931898c79dd --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_ov5642.c @@ -0,0 +1,1087 @@ +/* + * Driver for OV5642 CMOS Image Sensor from Omnivision + * + * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com> + * + * Based on Sony IMX074 Camera Driver + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * Based on Omnivision OV7670 Camera Driver + * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/module.h> +#include <linux/v4l2-mediabus.h> + +#include <media/soc_camera.h> +#include <media/v4l2-clk.h> +#include <media/v4l2-subdev.h> + +/* OV5642 registers */ +#define REG_CHIP_ID_HIGH 0x300a +#define REG_CHIP_ID_LOW 0x300b + +#define REG_WINDOW_START_X_HIGH 0x3800 +#define REG_WINDOW_START_X_LOW 0x3801 +#define REG_WINDOW_START_Y_HIGH 0x3802 +#define REG_WINDOW_START_Y_LOW 0x3803 +#define REG_WINDOW_WIDTH_HIGH 0x3804 +#define REG_WINDOW_WIDTH_LOW 0x3805 +#define REG_WINDOW_HEIGHT_HIGH 0x3806 +#define REG_WINDOW_HEIGHT_LOW 0x3807 +#define REG_OUT_WIDTH_HIGH 0x3808 +#define REG_OUT_WIDTH_LOW 0x3809 +#define REG_OUT_HEIGHT_HIGH 0x380a +#define REG_OUT_HEIGHT_LOW 0x380b +#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c +#define REG_OUT_TOTAL_WIDTH_LOW 0x380d +#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e +#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f +#define REG_OUTPUT_FORMAT 0x4300 +#define REG_ISP_CTRL_01 0x5001 +#define REG_AVG_WINDOW_END_X_HIGH 0x5682 +#define REG_AVG_WINDOW_END_X_LOW 0x5683 +#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 +#define REG_AVG_WINDOW_END_Y_LOW 0x5687 + +/* active pixel array size */ +#define OV5642_SENSOR_SIZE_X 2592 +#define OV5642_SENSOR_SIZE_Y 1944 + +/* + * About OV5642 resolution, cropping and binning: + * This sensor supports it all, at least in the feature description. + * Unfortunately, no combination of appropriate registers settings could make + * the chip work the intended way. As it works with predefined register lists, + * some undocumented registers are presumably changed there to achieve their + * goals. + * This driver currently only works for resolutions up to 720 lines with a + * 1:1 scale. Hopefully these restrictions will be removed in the future. + */ +#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X +#define OV5642_MAX_HEIGHT 720 + +/* default sizes */ +#define OV5642_DEFAULT_WIDTH 1280 +#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT + +/* minimum extra blanking */ +#define BLANKING_EXTRA_WIDTH 500 +#define BLANKING_EXTRA_HEIGHT 20 + +/* + * the sensor's autoexposure is buggy when setting total_height low. + * It tries to expose longer than 1 frame period without taking care of it + * and this leads to weird output. So we set 1000 lines as minimum. + */ +#define BLANKING_MIN_HEIGHT 1000 + +struct regval_list { + u16 reg_num; + u8 value; +}; + +static struct regval_list ov5642_default_regs_init[] = { + { 0x3103, 0x93 }, + { 0x3008, 0x82 }, + { 0x3017, 0x7f }, + { 0x3018, 0xfc }, + { 0x3810, 0xc2 }, + { 0x3615, 0xf0 }, + { 0x3000, 0x0 }, + { 0x3001, 0x0 }, + { 0x3002, 0x0 }, + { 0x3003, 0x0 }, + { 0x3004, 0xff }, + { 0x3030, 0x2b }, + { 0x3011, 0x8 }, + { 0x3010, 0x10 }, + { 0x3604, 0x60 }, + { 0x3622, 0x60 }, + { 0x3621, 0x9 }, + { 0x3709, 0x0 }, + { 0x4000, 0x21 }, + { 0x401d, 0x22 }, + { 0x3600, 0x54 }, + { 0x3605, 0x4 }, + { 0x3606, 0x3f }, + { 0x3c01, 0x80 }, + { 0x300d, 0x22 }, + { 0x3623, 0x22 }, + { 0x5000, 0x4f }, + { 0x5020, 0x4 }, + { 0x5181, 0x79 }, + { 0x5182, 0x0 }, + { 0x5185, 0x22 }, + { 0x5197, 0x1 }, + { 0x5500, 0xa }, + { 0x5504, 0x0 }, + { 0x5505, 0x7f }, + { 0x5080, 0x8 }, + { 0x300e, 0x18 }, + { 0x4610, 0x0 }, + { 0x471d, 0x5 }, + { 0x4708, 0x6 }, + { 0x370c, 0xa0 }, + { 0x5687, 0x94 }, + { 0x501f, 0x0 }, + { 0x5000, 0x4f }, + { 0x5001, 0xcf }, + { 0x4300, 0x30 }, + { 0x4300, 0x30 }, + { 0x460b, 0x35 }, + { 0x471d, 0x0 }, + { 0x3002, 0xc }, + { 0x3002, 0x0 }, + { 0x4713, 0x3 }, + { 0x471c, 0x50 }, + { 0x4721, 0x2 }, + { 0x4402, 0x90 }, + { 0x460c, 0x22 }, + { 0x3815, 0x44 }, + { 0x3503, 0x7 }, + { 0x3501, 0x73 }, + { 0x3502, 0x80 }, + { 0x350b, 0x0 }, + { 0x3818, 0xc8 }, + { 0x3824, 0x11 }, + { 0x3a00, 0x78 }, + { 0x3a1a, 0x4 }, + { 0x3a13, 0x30 }, + { 0x3a18, 0x0 }, + { 0x3a19, 0x7c }, + { 0x3a08, 0x12 }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0xf }, + { 0x3a0b, 0xa0 }, + { 0x350c, 0x7 }, + { 0x350d, 0xd0 }, + { 0x3a0d, 0x8 }, + { 0x3a0e, 0x6 }, + { 0x3500, 0x0 }, + { 0x3501, 0x0 }, + { 0x3502, 0x0 }, + { 0x350a, 0x0 }, + { 0x350b, 0x0 }, + { 0x3503, 0x0 }, + { 0x3a0f, 0x3c }, + { 0x3a10, 0x32 }, + { 0x3a1b, 0x3c }, + { 0x3a1e, 0x32 }, + { 0x3a11, 0x80 }, + { 0x3a1f, 0x20 }, + { 0x3030, 0x2b }, + { 0x3a02, 0x0 }, + { 0x3a03, 0x7d }, + { 0x3a04, 0x0 }, + { 0x3a14, 0x0 }, + { 0x3a15, 0x7d }, + { 0x3a16, 0x0 }, + { 0x3a00, 0x78 }, + { 0x3a08, 0x9 }, + { 0x3a09, 0x60 }, + { 0x3a0a, 0x7 }, + { 0x3a0b, 0xd0 }, + { 0x3a0d, 0x10 }, + { 0x3a0e, 0xd }, + { 0x4407, 0x4 }, + { 0x5193, 0x70 }, + { 0x589b, 0x0 }, + { 0x589a, 0xc0 }, + { 0x401e, 0x20 }, + { 0x4001, 0x42 }, + { 0x401c, 0x6 }, + { 0x3825, 0xac }, + { 0x3827, 0xc }, + { 0x528a, 0x1 }, + { 0x528b, 0x4 }, + { 0x528c, 0x8 }, + { 0x528d, 0x10 }, + { 0x528e, 0x20 }, + { 0x528f, 0x28 }, + { 0x5290, 0x30 }, + { 0x5292, 0x0 }, + { 0x5293, 0x1 }, + { 0x5294, 0x0 }, + { 0x5295, 0x4 }, + { 0x5296, 0x0 }, + { 0x5297, 0x8 }, + { 0x5298, 0x0 }, + { 0x5299, 0x10 }, + { 0x529a, 0x0 }, + { 0x529b, 0x20 }, + { 0x529c, 0x0 }, + { 0x529d, 0x28 }, + { 0x529e, 0x0 }, + { 0x529f, 0x30 }, + { 0x5282, 0x0 }, + { 0x5300, 0x0 }, + { 0x5301, 0x20 }, + { 0x5302, 0x0 }, + { 0x5303, 0x7c }, + { 0x530c, 0x0 }, + { 0x530d, 0xc }, + { 0x530e, 0x20 }, + { 0x530f, 0x80 }, + { 0x5310, 0x20 }, + { 0x5311, 0x80 }, + { 0x5308, 0x20 }, + { 0x5309, 0x40 }, + { 0x5304, 0x0 }, + { 0x5305, 0x30 }, + { 0x5306, 0x0 }, + { 0x5307, 0x80 }, + { 0x5314, 0x8 }, + { 0x5315, 0x20 }, + { 0x5319, 0x30 }, + { 0x5316, 0x10 }, + { 0x5317, 0x0 }, + { 0x5318, 0x2 }, + { 0x5380, 0x1 }, + { 0x5381, 0x0 }, + { 0x5382, 0x0 }, + { 0x5383, 0x4e }, + { 0x5384, 0x0 }, + { 0x5385, 0xf }, + { 0x5386, 0x0 }, + { 0x5387, 0x0 }, + { 0x5388, 0x1 }, + { 0x5389, 0x15 }, + { 0x538a, 0x0 }, + { 0x538b, 0x31 }, + { 0x538c, 0x0 }, + { 0x538d, 0x0 }, + { 0x538e, 0x0 }, + { 0x538f, 0xf }, + { 0x5390, 0x0 }, + { 0x5391, 0xab }, + { 0x5392, 0x0 }, + { 0x5393, 0xa2 }, + { 0x5394, 0x8 }, + { 0x5480, 0x14 }, + { 0x5481, 0x21 }, + { 0x5482, 0x36 }, + { 0x5483, 0x57 }, + { 0x5484, 0x65 }, + { 0x5485, 0x71 }, + { 0x5486, 0x7d }, + { 0x5487, 0x87 }, + { 0x5488, 0x91 }, + { 0x5489, 0x9a }, + { 0x548a, 0xaa }, + { 0x548b, 0xb8 }, + { 0x548c, 0xcd }, + { 0x548d, 0xdd }, + { 0x548e, 0xea }, + { 0x548f, 0x1d }, + { 0x5490, 0x5 }, + { 0x5491, 0x0 }, + { 0x5492, 0x4 }, + { 0x5493, 0x20 }, + { 0x5494, 0x3 }, + { 0x5495, 0x60 }, + { 0x5496, 0x2 }, + { 0x5497, 0xb8 }, + { 0x5498, 0x2 }, + { 0x5499, 0x86 }, + { 0x549a, 0x2 }, + { 0x549b, 0x5b }, + { 0x549c, 0x2 }, + { 0x549d, 0x3b }, + { 0x549e, 0x2 }, + { 0x549f, 0x1c }, + { 0x54a0, 0x2 }, + { 0x54a1, 0x4 }, + { 0x54a2, 0x1 }, + { 0x54a3, 0xed }, + { 0x54a4, 0x1 }, + { 0x54a5, 0xc5 }, + { 0x54a6, 0x1 }, + { 0x54a7, 0xa5 }, + { 0x54a8, 0x1 }, + { 0x54a9, 0x6c }, + { 0x54aa, 0x1 }, + { 0x54ab, 0x41 }, + { 0x54ac, 0x1 }, + { 0x54ad, 0x20 }, + { 0x54ae, 0x0 }, + { 0x54af, 0x16 }, + { 0x54b0, 0x1 }, + { 0x54b1, 0x20 }, + { 0x54b2, 0x0 }, + { 0x54b3, 0x10 }, + { 0x54b4, 0x0 }, + { 0x54b5, 0xf0 }, + { 0x54b6, 0x0 }, + { 0x54b7, 0xdf }, + { 0x5402, 0x3f }, + { 0x5403, 0x0 }, + { 0x3406, 0x0 }, + { 0x5180, 0xff }, + { 0x5181, 0x52 }, + { 0x5182, 0x11 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x6 }, + { 0x5187, 0x8 }, + { 0x5188, 0x8 }, + { 0x5189, 0x7c }, + { 0x518a, 0x60 }, + { 0x518b, 0xb2 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x44 }, + { 0x518e, 0x3d }, + { 0x518f, 0x58 }, + { 0x5190, 0x46 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x4 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x3 }, + { 0x5197, 0x1 }, + { 0x5198, 0x4 }, + { 0x5199, 0x12 }, + { 0x519a, 0x4 }, + { 0x519b, 0x0 }, + { 0x519c, 0x6 }, + { 0x519d, 0x82 }, + { 0x519e, 0x0 }, + { 0x5025, 0x80 }, + { 0x3a0f, 0x38 }, + { 0x3a10, 0x30 }, + { 0x3a1b, 0x3a }, + { 0x3a1e, 0x2e }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x10 }, + { 0x5688, 0xa6 }, + { 0x5689, 0x6a }, + { 0x568a, 0xea }, + { 0x568b, 0xae }, + { 0x568c, 0xa6 }, + { 0x568d, 0x6a }, + { 0x568e, 0x62 }, + { 0x568f, 0x26 }, + { 0x5583, 0x40 }, + { 0x5584, 0x40 }, + { 0x5580, 0x2 }, + { 0x5000, 0xcf }, + { 0x5800, 0x27 }, + { 0x5801, 0x19 }, + { 0x5802, 0x12 }, + { 0x5803, 0xf }, + { 0x5804, 0x10 }, + { 0x5805, 0x15 }, + { 0x5806, 0x1e }, + { 0x5807, 0x2f }, + { 0x5808, 0x15 }, + { 0x5809, 0xd }, + { 0x580a, 0xa }, + { 0x580b, 0x9 }, + { 0x580c, 0xa }, + { 0x580d, 0xc }, + { 0x580e, 0x12 }, + { 0x580f, 0x19 }, + { 0x5810, 0xb }, + { 0x5811, 0x7 }, + { 0x5812, 0x4 }, + { 0x5813, 0x3 }, + { 0x5814, 0x3 }, + { 0x5815, 0x6 }, + { 0x5816, 0xa }, + { 0x5817, 0xf }, + { 0x5818, 0xa }, + { 0x5819, 0x5 }, + { 0x581a, 0x1 }, + { 0x581b, 0x0 }, + { 0x581c, 0x0 }, + { 0x581d, 0x3 }, + { 0x581e, 0x8 }, + { 0x581f, 0xc }, + { 0x5820, 0xa }, + { 0x5821, 0x5 }, + { 0x5822, 0x1 }, + { 0x5823, 0x0 }, + { 0x5824, 0x0 }, + { 0x5825, 0x3 }, + { 0x5826, 0x8 }, + { 0x5827, 0xc }, + { 0x5828, 0xe }, + { 0x5829, 0x8 }, + { 0x582a, 0x6 }, + { 0x582b, 0x4 }, + { 0x582c, 0x5 }, + { 0x582d, 0x7 }, + { 0x582e, 0xb }, + { 0x582f, 0x12 }, + { 0x5830, 0x18 }, + { 0x5831, 0x10 }, + { 0x5832, 0xc }, + { 0x5833, 0xa }, + { 0x5834, 0xb }, + { 0x5835, 0xe }, + { 0x5836, 0x15 }, + { 0x5837, 0x19 }, + { 0x5838, 0x32 }, + { 0x5839, 0x1f }, + { 0x583a, 0x18 }, + { 0x583b, 0x16 }, + { 0x583c, 0x17 }, + { 0x583d, 0x1e }, + { 0x583e, 0x26 }, + { 0x583f, 0x53 }, + { 0x5840, 0x10 }, + { 0x5841, 0xf }, + { 0x5842, 0xd }, + { 0x5843, 0xc }, + { 0x5844, 0xe }, + { 0x5845, 0x9 }, + { 0x5846, 0x11 }, + { 0x5847, 0x10 }, + { 0x5848, 0x10 }, + { 0x5849, 0x10 }, + { 0x584a, 0x10 }, + { 0x584b, 0xe }, + { 0x584c, 0x10 }, + { 0x584d, 0x10 }, + { 0x584e, 0x11 }, + { 0x584f, 0x10 }, + { 0x5850, 0xf }, + { 0x5851, 0xc }, + { 0x5852, 0xf }, + { 0x5853, 0x10 }, + { 0x5854, 0x10 }, + { 0x5855, 0xf }, + { 0x5856, 0xe }, + { 0x5857, 0xb }, + { 0x5858, 0x10 }, + { 0x5859, 0xd }, + { 0x585a, 0xd }, + { 0x585b, 0xc }, + { 0x585c, 0xc }, + { 0x585d, 0xc }, + { 0x585e, 0xb }, + { 0x585f, 0xc }, + { 0x5860, 0xc }, + { 0x5861, 0xc }, + { 0x5862, 0xd }, + { 0x5863, 0x8 }, + { 0x5864, 0x11 }, + { 0x5865, 0x18 }, + { 0x5866, 0x18 }, + { 0x5867, 0x19 }, + { 0x5868, 0x17 }, + { 0x5869, 0x19 }, + { 0x586a, 0x16 }, + { 0x586b, 0x13 }, + { 0x586c, 0x13 }, + { 0x586d, 0x12 }, + { 0x586e, 0x13 }, + { 0x586f, 0x16 }, + { 0x5870, 0x14 }, + { 0x5871, 0x12 }, + { 0x5872, 0x10 }, + { 0x5873, 0x11 }, + { 0x5874, 0x11 }, + { 0x5875, 0x16 }, + { 0x5876, 0x14 }, + { 0x5877, 0x11 }, + { 0x5878, 0x10 }, + { 0x5879, 0xf }, + { 0x587a, 0x10 }, + { 0x587b, 0x14 }, + { 0x587c, 0x13 }, + { 0x587d, 0x12 }, + { 0x587e, 0x11 }, + { 0x587f, 0x11 }, + { 0x5880, 0x12 }, + { 0x5881, 0x15 }, + { 0x5882, 0x14 }, + { 0x5883, 0x15 }, + { 0x5884, 0x15 }, + { 0x5885, 0x15 }, + { 0x5886, 0x13 }, + { 0x5887, 0x17 }, + { 0x3710, 0x10 }, + { 0x3632, 0x51 }, + { 0x3702, 0x10 }, + { 0x3703, 0xb2 }, + { 0x3704, 0x18 }, + { 0x370b, 0x40 }, + { 0x370d, 0x3 }, + { 0x3631, 0x1 }, + { 0x3632, 0x52 }, + { 0x3606, 0x24 }, + { 0x3620, 0x96 }, + { 0x5785, 0x7 }, + { 0x3a13, 0x30 }, + { 0x3600, 0x52 }, + { 0x3604, 0x48 }, + { 0x3606, 0x1b }, + { 0x370d, 0xb }, + { 0x370f, 0xc0 }, + { 0x3709, 0x1 }, + { 0x3823, 0x0 }, + { 0x5007, 0x0 }, + { 0x5009, 0x0 }, + { 0x5011, 0x0 }, + { 0x5013, 0x0 }, + { 0x519e, 0x0 }, + { 0x5086, 0x0 }, + { 0x5087, 0x0 }, + { 0x5088, 0x0 }, + { 0x5089, 0x0 }, + { 0x302b, 0x0 }, + { 0x3503, 0x7 }, + { 0x3011, 0x8 }, + { 0x350c, 0x2 }, + { 0x350d, 0xe4 }, + { 0x3621, 0xc9 }, + { 0x370a, 0x81 }, + { 0xffff, 0xff }, +}; + +static struct regval_list ov5642_default_regs_finalise[] = { + { 0x3810, 0xc2 }, + { 0x3818, 0xc9 }, + { 0x381c, 0x10 }, + { 0x381d, 0xa0 }, + { 0x381e, 0x5 }, + { 0x381f, 0xb0 }, + { 0x3820, 0x0 }, + { 0x3821, 0x0 }, + { 0x3824, 0x11 }, + { 0x3a08, 0x1b }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0x17 }, + { 0x3a0b, 0x20 }, + { 0x3a0d, 0x2 }, + { 0x3a0e, 0x1 }, + { 0x401c, 0x4 }, + { 0x5682, 0x5 }, + { 0x5683, 0x0 }, + { 0x5686, 0x2 }, + { 0x5687, 0xcc }, + { 0x5001, 0x4f }, + { 0x589b, 0x6 }, + { 0x589a, 0xc5 }, + { 0x3503, 0x0 }, + { 0x460c, 0x20 }, + { 0x460b, 0x37 }, + { 0x471c, 0xd0 }, + { 0x471d, 0x5 }, + { 0x3815, 0x1 }, + { 0x3818, 0xc1 }, + { 0x501f, 0x0 }, + { 0x5002, 0xe0 }, + { 0x4300, 0x32 }, /* UYVY */ + { 0x3002, 0x1c }, + { 0x4800, 0x14 }, + { 0x4801, 0xf }, + { 0x3007, 0x3b }, + { 0x300e, 0x4 }, + { 0x4803, 0x50 }, + { 0x3815, 0x1 }, + { 0x4713, 0x2 }, + { 0x4842, 0x1 }, + { 0x300f, 0xe }, + { 0x3003, 0x3 }, + { 0x3003, 0x1 }, + { 0xffff, 0xff }, +}; + +struct ov5642_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +struct ov5642 { + struct v4l2_subdev subdev; + const struct ov5642_datafmt *fmt; + struct v4l2_rect crop_rect; + struct v4l2_clk *clk; + + /* blanking information */ + int total_width; + int total_height; +}; + +static const struct ov5642_datafmt ov5642_colour_fmts[] = { + {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, +}; + +static struct ov5642 *to_ov5642(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov5642, subdev); +} + +/* Find a data format by a pixel code in an array */ +static const struct ov5642_datafmt + *ov5642_find_datafmt(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) + if (ov5642_colour_fmts[i].code == code) + return ov5642_colour_fmts + i; + + return NULL; +} + +static int reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + /* We have 16-bit i2c addresses - care for endianness */ + unsigned char data[2] = { reg >> 8, reg & 0xff }; + + ret = i2c_master_send(client, data, 2); + if (ret < 2) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 1) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + return 0; +} + +static int reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val }; + + ret = i2c_master_send(client, data, 3); + if (ret < 3) { + dev_err(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/* + * convenience function to write 16 bit register values that are split up + * into two consecutive high and low parts + */ +static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) +{ + int ret; + + ret = reg_write(client, reg, val16 >> 8); + if (ret) + return ret; + return reg_write(client, reg + 1, val16 & 0x00ff); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 1; + + ret = reg_read(client, reg->reg, &val); + if (!ret) + reg->val = (__u64)val; + + return ret; +} + +static int ov5642_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov5642_write_array(struct i2c_client *client, + struct regval_list *vals) +{ + while (vals->reg_num != 0xffff || vals->value != 0xff) { + int ret = reg_write(client, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + dev_dbg(&client->dev, "Register list loaded\n"); + return 0; +} + +static int ov5642_set_resolution(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + int width = priv->crop_rect.width; + int height = priv->crop_rect.height; + int total_width = priv->total_width; + int total_height = priv->total_height; + int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; + int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; + int ret; + + /* + * This should set the starting point for cropping. + * Doesn't work so far. + */ + ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); + if (!ret) + ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); + if (!ret) { + priv->crop_rect.left = start_x; + priv->crop_rect.top = start_y; + } + + if (!ret) + ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); + if (ret) + return ret; + priv->crop_rect.width = width; + priv->crop_rect.height = height; + + /* Set the output window size. Only 1:1 scale is supported so far. */ + ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); + + /* Total width = output size + blanking */ + if (!ret) + ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); + if (!ret) + ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); + + /* Sets the window for AWB calculations */ + if (!ret) + ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); + + return ret; +} + +static int ov5642_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); + + if (format->pad) + return -EINVAL; + + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; + + if (!fmt) { + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mf->code = ov5642_colour_fmts[0].code; + mf->colorspace = ov5642_colour_fmts[0].colorspace; + } + + mf->field = V4L2_FIELD_NONE; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->fmt = fmt; + else + cfg->try_fmt = *mf; + return 0; +} + +static int ov5642_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + const struct ov5642_datafmt *fmt = priv->fmt; + + if (format->pad) + return -EINVAL; + + mf->code = fmt->code; + mf->colorspace = fmt->colorspace; + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) + return -EINVAL; + + code->code = ov5642_colour_fmts[code->index].code; + return 0; +} + +static int ov5642_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + struct v4l2_rect rect = sel->r; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, + &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); + + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; + priv->total_height = max_t(int, rect.height + + BLANKING_EXTRA_HEIGHT, + BLANKING_MIN_HEIGHT); + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + +static int ov5642_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV5642_MAX_WIDTH; + sel->r.height = OV5642_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->crop_rect; + return 0; + default: + return -EINVAL; + } +} + +static int ov5642_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + return 0; +} + +static int ov5642_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov5642 *priv = to_ov5642(client); + int ret; + + if (!on) + return soc_camera_power_off(&client->dev, ssdd, priv->clk); + + ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); + if (ret < 0) + return ret; + + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { + .g_mbus_config = ov5642_g_mbus_config, +}; + +static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { + .enum_mbus_code = ov5642_enum_mbus_code, + .get_selection = ov5642_get_selection, + .set_selection = ov5642_set_selection, + .get_fmt = ov5642_get_fmt, + .set_fmt = ov5642_set_fmt, +}; + +static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { + .s_power = ov5642_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov5642_get_register, + .s_register = ov5642_set_register, +#endif +}; + +static const struct v4l2_subdev_ops ov5642_subdev_ops = { + .core = &ov5642_subdev_core_ops, + .video = &ov5642_subdev_video_ops, + .pad = &ov5642_subdev_pad_ops, +}; + +static int ov5642_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + int ret; + u8 id_high, id_low; + u16 id; + + ret = ov5642_s_power(subdev, 1); + if (ret < 0) + return ret; + + /* Read sensor Model ID */ + ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); + if (ret < 0) + goto done; + + id = id_high << 8; + + ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); + if (ret < 0) + goto done; + + id |= id_low; + + dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); + + if (id != 0x5642) { + ret = -ENODEV; + goto done; + } + + ret = 0; + +done: + ov5642_s_power(subdev, 0); + return ret; +} + +static int ov5642_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov5642 *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "OV5642: missing platform data!\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); + + priv->fmt = &ov5642_colour_fmts[0]; + + priv->crop_rect.width = OV5642_DEFAULT_WIDTH; + priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; + priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; + priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; + priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; + priv->total_height = BLANKING_MIN_HEIGHT; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = ov5642_video_probe(client); + if (ret < 0) + v4l2_clk_put(priv->clk); + + return ret; +} + +static int ov5642_remove(struct i2c_client *client) +{ + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov5642 *priv = to_ov5642(client); + + v4l2_clk_put(priv->clk); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + + return 0; +} + +static const struct i2c_device_id ov5642_id[] = { + { "ov5642", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5642_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5642_of_match[] = { + { .compatible = "ovti,ov5642" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ov5642_of_match); +#endif + +static struct i2c_driver ov5642_i2c_driver = { + .driver = { + .name = "ov5642", + .of_match_table = of_match_ptr(ov5642_of_match), + }, + .probe = ov5642_probe, + .remove = ov5642_remove, + .id_table = ov5642_id, +}; + +module_i2c_driver(ov5642_i2c_driver); + +MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); +MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c new file mode 100644 index 000000000000..a07d3145d1b4 --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_ov9740.c @@ -0,0 +1,996 @@ +/* + * OmniVision OV9740 Camera Driver + * + * Copyright (C) 2011 NVIDIA Corporation + * + * Based on ov9640 camera driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> + +#include <media/soc_camera.h> +#include <media/v4l2-clk.h> +#include <media/v4l2-ctrls.h> + +#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) + +/* General Status Registers */ +#define OV9740_MODEL_ID_HI 0x0000 +#define OV9740_MODEL_ID_LO 0x0001 +#define OV9740_REVISION_NUMBER 0x0002 +#define OV9740_MANUFACTURER_ID 0x0003 +#define OV9740_SMIA_VERSION 0x0004 + +/* General Setup Registers */ +#define OV9740_MODE_SELECT 0x0100 +#define OV9740_IMAGE_ORT 0x0101 +#define OV9740_SOFTWARE_RESET 0x0103 +#define OV9740_GRP_PARAM_HOLD 0x0104 +#define OV9740_MSK_CORRUP_FM 0x0105 + +/* Timing Setting */ +#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ +#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ +#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ +#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ +#define OV9740_X_ADDR_START_HI 0x0344 +#define OV9740_X_ADDR_START_LO 0x0345 +#define OV9740_Y_ADDR_START_HI 0x0346 +#define OV9740_Y_ADDR_START_LO 0x0347 +#define OV9740_X_ADDR_END_HI 0x0348 +#define OV9740_X_ADDR_END_LO 0x0349 +#define OV9740_Y_ADDR_END_HI 0x034a +#define OV9740_Y_ADDR_END_LO 0x034b +#define OV9740_X_OUTPUT_SIZE_HI 0x034c +#define OV9740_X_OUTPUT_SIZE_LO 0x034d +#define OV9740_Y_OUTPUT_SIZE_HI 0x034e +#define OV9740_Y_OUTPUT_SIZE_LO 0x034f + +/* IO Control Registers */ +#define OV9740_IO_CREL00 0x3002 +#define OV9740_IO_CREL01 0x3004 +#define OV9740_IO_CREL02 0x3005 +#define OV9740_IO_OUTPUT_SEL01 0x3026 +#define OV9740_IO_OUTPUT_SEL02 0x3027 + +/* AWB Registers */ +#define OV9740_AWB_MANUAL_CTRL 0x3406 + +/* Analog Control Registers */ +#define OV9740_ANALOG_CTRL01 0x3601 +#define OV9740_ANALOG_CTRL02 0x3602 +#define OV9740_ANALOG_CTRL03 0x3603 +#define OV9740_ANALOG_CTRL04 0x3604 +#define OV9740_ANALOG_CTRL10 0x3610 +#define OV9740_ANALOG_CTRL12 0x3612 +#define OV9740_ANALOG_CTRL15 0x3615 +#define OV9740_ANALOG_CTRL20 0x3620 +#define OV9740_ANALOG_CTRL21 0x3621 +#define OV9740_ANALOG_CTRL22 0x3622 +#define OV9740_ANALOG_CTRL30 0x3630 +#define OV9740_ANALOG_CTRL31 0x3631 +#define OV9740_ANALOG_CTRL32 0x3632 +#define OV9740_ANALOG_CTRL33 0x3633 + +/* Sensor Control */ +#define OV9740_SENSOR_CTRL03 0x3703 +#define OV9740_SENSOR_CTRL04 0x3704 +#define OV9740_SENSOR_CTRL05 0x3705 +#define OV9740_SENSOR_CTRL07 0x3707 + +/* Timing Control */ +#define OV9740_TIMING_CTRL17 0x3817 +#define OV9740_TIMING_CTRL19 0x3819 +#define OV9740_TIMING_CTRL33 0x3833 +#define OV9740_TIMING_CTRL35 0x3835 + +/* Banding Filter */ +#define OV9740_AEC_MAXEXPO_60_H 0x3a02 +#define OV9740_AEC_MAXEXPO_60_L 0x3a03 +#define OV9740_AEC_B50_STEP_HI 0x3a08 +#define OV9740_AEC_B50_STEP_LO 0x3a09 +#define OV9740_AEC_B60_STEP_HI 0x3a0a +#define OV9740_AEC_B60_STEP_LO 0x3a0b +#define OV9740_AEC_CTRL0D 0x3a0d +#define OV9740_AEC_CTRL0E 0x3a0e +#define OV9740_AEC_MAXEXPO_50_H 0x3a14 +#define OV9740_AEC_MAXEXPO_50_L 0x3a15 + +/* AEC/AGC Control */ +#define OV9740_AEC_ENABLE 0x3503 +#define OV9740_GAIN_CEILING_01 0x3a18 +#define OV9740_GAIN_CEILING_02 0x3a19 +#define OV9740_AEC_HI_THRESHOLD 0x3a11 +#define OV9740_AEC_3A1A 0x3a1a +#define OV9740_AEC_CTRL1B_WPT2 0x3a1b +#define OV9740_AEC_CTRL0F_WPT 0x3a0f +#define OV9740_AEC_CTRL10_BPT 0x3a10 +#define OV9740_AEC_CTRL1E_BPT2 0x3a1e +#define OV9740_AEC_LO_THRESHOLD 0x3a1f + +/* BLC Control */ +#define OV9740_BLC_AUTO_ENABLE 0x4002 +#define OV9740_BLC_MODE 0x4005 + +/* VFIFO */ +#define OV9740_VFIFO_READ_START_HI 0x4608 +#define OV9740_VFIFO_READ_START_LO 0x4609 + +/* DVP Control */ +#define OV9740_DVP_VSYNC_CTRL02 0x4702 +#define OV9740_DVP_VSYNC_MODE 0x4704 +#define OV9740_DVP_VSYNC_CTRL06 0x4706 + +/* PLL Setting */ +#define OV9740_PLL_MODE_CTRL01 0x3104 +#define OV9740_PRE_PLL_CLK_DIV 0x0305 +#define OV9740_PLL_MULTIPLIER 0x0307 +#define OV9740_VT_SYS_CLK_DIV 0x0303 +#define OV9740_VT_PIX_CLK_DIV 0x0301 +#define OV9740_PLL_CTRL3010 0x3010 +#define OV9740_VFIFO_CTRL00 0x460e + +/* ISP Control */ +#define OV9740_ISP_CTRL00 0x5000 +#define OV9740_ISP_CTRL01 0x5001 +#define OV9740_ISP_CTRL03 0x5003 +#define OV9740_ISP_CTRL05 0x5005 +#define OV9740_ISP_CTRL12 0x5012 +#define OV9740_ISP_CTRL19 0x5019 +#define OV9740_ISP_CTRL1A 0x501a +#define OV9740_ISP_CTRL1E 0x501e +#define OV9740_ISP_CTRL1F 0x501f +#define OV9740_ISP_CTRL20 0x5020 +#define OV9740_ISP_CTRL21 0x5021 + +/* AWB */ +#define OV9740_AWB_CTRL00 0x5180 +#define OV9740_AWB_CTRL01 0x5181 +#define OV9740_AWB_CTRL02 0x5182 +#define OV9740_AWB_CTRL03 0x5183 +#define OV9740_AWB_ADV_CTRL01 0x5184 +#define OV9740_AWB_ADV_CTRL02 0x5185 +#define OV9740_AWB_ADV_CTRL03 0x5186 +#define OV9740_AWB_ADV_CTRL04 0x5187 +#define OV9740_AWB_ADV_CTRL05 0x5188 +#define OV9740_AWB_ADV_CTRL06 0x5189 +#define OV9740_AWB_ADV_CTRL07 0x518a +#define OV9740_AWB_ADV_CTRL08 0x518b +#define OV9740_AWB_ADV_CTRL09 0x518c +#define OV9740_AWB_ADV_CTRL10 0x518d +#define OV9740_AWB_ADV_CTRL11 0x518e +#define OV9740_AWB_CTRL0F 0x518f +#define OV9740_AWB_CTRL10 0x5190 +#define OV9740_AWB_CTRL11 0x5191 +#define OV9740_AWB_CTRL12 0x5192 +#define OV9740_AWB_CTRL13 0x5193 +#define OV9740_AWB_CTRL14 0x5194 + +/* MIPI Control */ +#define OV9740_MIPI_CTRL00 0x4800 +#define OV9740_MIPI_3837 0x3837 +#define OV9740_MIPI_CTRL01 0x4801 +#define OV9740_MIPI_CTRL03 0x4803 +#define OV9740_MIPI_CTRL05 0x4805 +#define OV9740_VFIFO_RD_CTRL 0x4601 +#define OV9740_MIPI_CTRL_3012 0x3012 +#define OV9740_SC_CMMM_MIPI_CTR 0x3014 + +#define OV9740_MAX_WIDTH 1280 +#define OV9740_MAX_HEIGHT 720 + +/* Misc. structures */ +struct ov9740_reg { + u16 reg; + u8 val; +}; + +struct ov9740_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_clk *clk; + + u16 model; + u8 revision; + u8 manid; + u8 smiaver; + + bool flag_vflip; + bool flag_hflip; + + /* For suspend/resume. */ + struct v4l2_mbus_framefmt current_mf; + bool current_enable; +}; + +static const struct ov9740_reg ov9740_defaults[] = { + /* Software Reset */ + { OV9740_SOFTWARE_RESET, 0x01 }, + + /* Banding Filter */ + { OV9740_AEC_B50_STEP_HI, 0x00 }, + { OV9740_AEC_B50_STEP_LO, 0xe8 }, + { OV9740_AEC_CTRL0E, 0x03 }, + { OV9740_AEC_MAXEXPO_50_H, 0x15 }, + { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, + { OV9740_AEC_B60_STEP_HI, 0x00 }, + { OV9740_AEC_B60_STEP_LO, 0xc0 }, + { OV9740_AEC_CTRL0D, 0x04 }, + { OV9740_AEC_MAXEXPO_60_H, 0x18 }, + { OV9740_AEC_MAXEXPO_60_L, 0x20 }, + + /* LC */ + { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, + { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, + + /* Un-documented OV9740 registers */ + { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, + { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, + { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, + { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, + { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, + { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, + { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, + { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, + { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, + { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, + { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, + { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, + { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, + { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, + { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, + { 0x583c, 0x5f }, + + /* Y Gamma */ + { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, + { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, + { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, + { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, + + /* UV Gamma */ + { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, + { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, + { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, + { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, + { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, + { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, + { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, + { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, + + /* AWB */ + { OV9740_AWB_CTRL00, 0xf0 }, + { OV9740_AWB_CTRL01, 0x00 }, + { OV9740_AWB_CTRL02, 0x41 }, + { OV9740_AWB_CTRL03, 0x42 }, + { OV9740_AWB_ADV_CTRL01, 0x8a }, + { OV9740_AWB_ADV_CTRL02, 0x61 }, + { OV9740_AWB_ADV_CTRL03, 0xce }, + { OV9740_AWB_ADV_CTRL04, 0xa8 }, + { OV9740_AWB_ADV_CTRL05, 0x17 }, + { OV9740_AWB_ADV_CTRL06, 0x1f }, + { OV9740_AWB_ADV_CTRL07, 0x27 }, + { OV9740_AWB_ADV_CTRL08, 0x41 }, + { OV9740_AWB_ADV_CTRL09, 0x34 }, + { OV9740_AWB_ADV_CTRL10, 0xf0 }, + { OV9740_AWB_ADV_CTRL11, 0x10 }, + { OV9740_AWB_CTRL0F, 0xff }, + { OV9740_AWB_CTRL10, 0x00 }, + { OV9740_AWB_CTRL11, 0xff }, + { OV9740_AWB_CTRL12, 0x00 }, + { OV9740_AWB_CTRL13, 0xff }, + { OV9740_AWB_CTRL14, 0x00 }, + + /* CIP */ + { 0x530d, 0x12 }, + + /* CMX */ + { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, + { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, + { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, + { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, + { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, + { 0x5394, 0x18 }, + + /* 50/60 Detection */ + { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, + + /* Output Select */ + { OV9740_IO_OUTPUT_SEL01, 0x00 }, + { OV9740_IO_OUTPUT_SEL02, 0x00 }, + { OV9740_IO_CREL00, 0x00 }, + { OV9740_IO_CREL01, 0x00 }, + { OV9740_IO_CREL02, 0x00 }, + + /* AWB Control */ + { OV9740_AWB_MANUAL_CTRL, 0x00 }, + + /* Analog Control */ + { OV9740_ANALOG_CTRL03, 0xaa }, + { OV9740_ANALOG_CTRL32, 0x2f }, + { OV9740_ANALOG_CTRL20, 0x66 }, + { OV9740_ANALOG_CTRL21, 0xc0 }, + { OV9740_ANALOG_CTRL31, 0x52 }, + { OV9740_ANALOG_CTRL33, 0x50 }, + { OV9740_ANALOG_CTRL30, 0xca }, + { OV9740_ANALOG_CTRL04, 0x0c }, + { OV9740_ANALOG_CTRL01, 0x40 }, + { OV9740_ANALOG_CTRL02, 0x16 }, + { OV9740_ANALOG_CTRL10, 0xa1 }, + { OV9740_ANALOG_CTRL12, 0x24 }, + { OV9740_ANALOG_CTRL22, 0x9f }, + { OV9740_ANALOG_CTRL15, 0xf0 }, + + /* Sensor Control */ + { OV9740_SENSOR_CTRL03, 0x42 }, + { OV9740_SENSOR_CTRL04, 0x10 }, + { OV9740_SENSOR_CTRL05, 0x45 }, + { OV9740_SENSOR_CTRL07, 0x14 }, + + /* Timing Control */ + { OV9740_TIMING_CTRL33, 0x04 }, + { OV9740_TIMING_CTRL35, 0x02 }, + { OV9740_TIMING_CTRL19, 0x6e }, + { OV9740_TIMING_CTRL17, 0x94 }, + + /* AEC/AGC Control */ + { OV9740_AEC_ENABLE, 0x10 }, + { OV9740_GAIN_CEILING_01, 0x00 }, + { OV9740_GAIN_CEILING_02, 0x7f }, + { OV9740_AEC_HI_THRESHOLD, 0xa0 }, + { OV9740_AEC_3A1A, 0x05 }, + { OV9740_AEC_CTRL1B_WPT2, 0x50 }, + { OV9740_AEC_CTRL0F_WPT, 0x50 }, + { OV9740_AEC_CTRL10_BPT, 0x4c }, + { OV9740_AEC_CTRL1E_BPT2, 0x4c }, + { OV9740_AEC_LO_THRESHOLD, 0x26 }, + + /* BLC Control */ + { OV9740_BLC_AUTO_ENABLE, 0x45 }, + { OV9740_BLC_MODE, 0x18 }, + + /* DVP Control */ + { OV9740_DVP_VSYNC_CTRL02, 0x04 }, + { OV9740_DVP_VSYNC_MODE, 0x00 }, + { OV9740_DVP_VSYNC_CTRL06, 0x08 }, + + /* PLL Setting */ + { OV9740_PLL_MODE_CTRL01, 0x20 }, + { OV9740_PRE_PLL_CLK_DIV, 0x03 }, + { OV9740_PLL_MULTIPLIER, 0x4c }, + { OV9740_VT_SYS_CLK_DIV, 0x01 }, + { OV9740_VT_PIX_CLK_DIV, 0x08 }, + { OV9740_PLL_CTRL3010, 0x01 }, + { OV9740_VFIFO_CTRL00, 0x82 }, + + /* Timing Setting */ + /* VTS */ + { OV9740_FRM_LENGTH_LN_HI, 0x03 }, + { OV9740_FRM_LENGTH_LN_LO, 0x07 }, + /* HTS */ + { OV9740_LN_LENGTH_PCK_HI, 0x06 }, + { OV9740_LN_LENGTH_PCK_LO, 0x62 }, + + /* MIPI Control */ + { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ + { OV9740_MIPI_3837, 0x01 }, + { OV9740_MIPI_CTRL01, 0x0f }, + { OV9740_MIPI_CTRL03, 0x05 }, + { OV9740_MIPI_CTRL05, 0x10 }, + { OV9740_VFIFO_RD_CTRL, 0x16 }, + { OV9740_MIPI_CTRL_3012, 0x70 }, + { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, + + /* YUYV order */ + { OV9740_ISP_CTRL19, 0x02 }, +}; + +static u32 ov9740_codes[] = { + MEDIA_BUS_FMT_YUYV8_2X8, +}; + +/* read a register */ +static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = (u8 *)®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = val, + }, + }; + + reg = swab16(reg); + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + +/* write a register */ +static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + struct i2c_msg msg; + struct { + u16 reg; + u8 val; + } __packed buf; + int ret; + + reg = swab16(reg); + + buf.reg = reg; + buf.val = val; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9740_reg_read(client, reg, &val); + if (ret < 0) { + dev_err(&client->dev, + "[Read]-Modify-Write of register 0x%04x failed!\n", + reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov9740_reg_write(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, + "Read-Modify-[Write] of register 0x%04x failed!\n", + reg); + return ret; + } + + return 0; +} + +static int ov9740_reg_write_array(struct i2c_client *client, + const struct ov9740_reg *regarray, + int regarraylen) +{ + int i; + int ret; + + for (i = 0; i < regarraylen; i++) { + ret = ov9740_reg_write(client, + regarray[i].reg, regarray[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Start/Stop streaming from the device */ +static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + /* Program orientation register. */ + if (priv->flag_vflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); + if (ret < 0) + return ret; + + if (priv->flag_hflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); + if (ret < 0) + return ret; + + if (enable) { + dev_dbg(&client->dev, "Enabling Streaming\n"); + /* Start Streaming */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); + + } else { + dev_dbg(&client->dev, "Disabling Streaming\n"); + /* Software Reset */ + ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); + if (!ret) + /* Setting Streaming to Standby */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, + 0x00); + } + + priv->current_enable = enable; + + return ret; +} + +/* select nearest higher resolution for capture */ +static void ov9740_res_roundup(u32 *width, u32 *height) +{ + /* Width must be a multiple of 4 pixels. */ + *width = ALIGN(*width, 4); + + /* Max resolution is 1280x720 (720p). */ + if (*width > OV9740_MAX_WIDTH) + *width = OV9740_MAX_WIDTH; + + if (*height > OV9740_MAX_HEIGHT) + *height = OV9740_MAX_HEIGHT; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) +{ + u32 x_start; + u32 y_start; + u32 x_end; + u32 y_end; + bool scaling = false; + u32 scale_input_x; + u32 scale_input_y; + int ret; + + if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) + scaling = true; + + /* + * Try to use as much of the sensor area as possible when supporting + * smaller resolutions. Depending on the aspect ratio of the + * chosen resolution, we can either use the full width of the sensor, + * or the full height of the sensor (or both if the aspect ratio is + * the same as 1280x720. + */ + if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { + scale_input_x = (OV9740_MAX_HEIGHT * width) / height; + scale_input_y = OV9740_MAX_HEIGHT; + } else { + scale_input_x = OV9740_MAX_WIDTH; + scale_input_y = (OV9740_MAX_WIDTH * height) / width; + } + + /* These describe the area of the sensor to use. */ + x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; + y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; + x_end = x_start + scale_input_x - 1; + y_end = y_start + scale_input_y - 1; + + ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, + (scale_input_x - width) >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, + (scale_input_x - width) & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | + (scaling << 4)); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); + +done: + return ret; +} + +/* set the format we will capture in */ +static int ov9740_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + ret = ov9740_reg_write_array(client, ov9740_defaults, + ARRAY_SIZE(ov9740_defaults)); + if (ret < 0) + return ret; + + ret = ov9740_set_res(client, mf->width, mf->height); + if (ret < 0) + return ret; + + priv->current_mf = *mf; + return ret; +} + +static int ov9740_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + ov9740_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + mf->code = MEDIA_BUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9740_s_fmt(sd, mf); + cfg->try_fmt = *mf; + return 0; +} + +static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) + return -EINVAL; + + code->code = ov9740_codes[code->index]; + + return 0; +} + +static int ov9740_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV9740_MAX_WIDTH; + sel->r.height = OV9740_MAX_HEIGHT; + return 0; + default: + return -EINVAL; + } +} + +/* Set status of additional camera capabilities */ +static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9740_priv *priv = + container_of(ctrl->handler, struct ov9740_priv, hdl); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->val; + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov9740_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + if (on) { + ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); + if (ret < 0) + return ret; + + if (priv->current_enable) { + ov9740_s_fmt(sd, &priv->current_mf); + ov9740_s_stream(sd, 1); + } + } else { + if (priv->current_enable) { + ov9740_s_stream(sd, 0); + priv->current_enable = true; + } + + soc_camera_power_off(&client->dev, ssdd, priv->clk); + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9740_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 2; + + ret = ov9740_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return ret; +} + +static int ov9740_set_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return ov9740_reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov9740_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9740_priv *priv = to_ov9740(sd); + u8 modelhi, modello; + int ret; + + ret = ov9740_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* + * check and show product ID and manufacturer ID + */ + ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); + if (ret < 0) + goto done; + + priv->model = (modelhi << 8) | modello; + + ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); + if (ret < 0) + goto done; + + if (priv->model != 0x9740) { + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", + priv->model, priv->revision, priv->manid, priv->smiaver); + + ret = v4l2_ctrl_handler_setup(&priv->hdl); + +done: + ov9740_s_power(&priv->subdev, 0); + return ret; +} + +/* Request bus settings on camera side */ +static int ov9740_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov9740_video_ops = { + .s_stream = ov9740_s_stream, + .g_mbus_config = ov9740_g_mbus_config, +}; + +static const struct v4l2_subdev_core_ops ov9740_core_ops = { + .s_power = ov9740_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9740_get_register, + .s_register = ov9740_set_register, +#endif +}; + +static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { + .enum_mbus_code = ov9740_enum_mbus_code, + .get_selection = ov9740_get_selection, + .set_fmt = ov9740_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9740_subdev_ops = { + .core = &ov9740_core_ops, + .video = &ov9740_video_ops, + .pad = &ov9740_pad_ops, +}; + +static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { + .s_ctrl = ov9740_s_ctrl, +}; + +/* + * i2c_driver function + */ +static int ov9740_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9740_priv *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 13); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) + return priv->hdl.error; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto eclkget; + } + + ret = ov9740_video_probe(client); + if (ret < 0) { + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); + } + + return ret; +} + +static int ov9740_remove(struct i2c_client *client) +{ + struct ov9740_priv *priv = i2c_get_clientdata(client); + + v4l2_clk_put(priv->clk); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); + return 0; +} + +static const struct i2c_device_id ov9740_id[] = { + { "ov9740", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9740_id); + +static struct i2c_driver ov9740_i2c_driver = { + .driver = { + .name = "ov9740", + }, + .probe = ov9740_probe, + .remove = ov9740_remove, + .id_table = ov9740_id, +}; + +module_i2c_driver(ov9740_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); +MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO index a951b3fd1ea1..ec277ece47af 100644 --- a/drivers/staging/media/sunxi/cedrus/TODO +++ b/drivers/staging/media/sunxi/cedrus/TODO @@ -5,8 +5,3 @@ Before this stateless decoder driver can leave the staging area: * Userspace support for the Request API needs to be reviewed; * Another stateless decoder driver should be submitted; * At least one stateless encoder driver should be submitted. -* When queueing a request containing references to I frames, the - refcount of the memory for those I frames needs to be incremented - and decremented when the request is completed. This will likely - require some help from vb2. The driver should fail the request - if the memory/buffer is gone. diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 3acfdcf83691..4aedd24a9848 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -140,11 +140,14 @@ static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, } static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx, - unsigned int index, - unsigned int plane) + int index, unsigned int plane) { - struct vb2_buffer *buf = ctx->dst_bufs[index]; + struct vb2_buffer *buf; + if (index < 0) + return 0; + + buf = ctx->dst_bufs[index]; return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 591d191d4286..4d6d602cdde6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -50,6 +50,8 @@ void cedrus_device_run(void *priv) break; } + v4l2_m2m_buf_copy_metadata(run.src, run.dst, true); + dev->dec_ops[ctx->current_codec]->setup(ctx, &run); /* Complete request(s) controls if needed. */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index 4f423d3a1cad..d1ae7903677b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,12 +16,6 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ -extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; - -void cedrus_device_work(struct work_struct *work); void cedrus_device_run(void *priv); -int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 300339fee1bc..0acf219a8c91 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -157,14 +157,14 @@ int cedrus_hw_probe(struct cedrus_dev *dev) irq_dec = platform_get_irq(dev->pdev, 0); if (irq_dec <= 0) { - v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n"); + dev_err(dev->dev, "Failed to get IRQ\n"); return irq_dec; } ret = devm_request_irq(dev->dev, irq_dec, cedrus_irq, 0, dev_name(dev->dev), dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n"); + dev_err(dev->dev, "Failed to request IRQ\n"); return ret; } @@ -182,21 +182,21 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = of_reserved_mem_device_init(dev->dev); if (ret && ret != -ENODEV) { - v4l2_err(&dev->v4l2_dev, "Failed to reserve memory\n"); + dev_err(dev->dev, "Failed to reserve memory\n"); return ret; } ret = sunxi_sram_claim(dev->dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n"); + dev_err(dev->dev, "Failed to claim SRAM\n"); goto err_mem; } dev->ahb_clk = devm_clk_get(dev->dev, "ahb"); if (IS_ERR(dev->ahb_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n"); + dev_err(dev->dev, "Failed to get AHB clock\n"); ret = PTR_ERR(dev->ahb_clk); goto err_sram; @@ -204,7 +204,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->mod_clk = devm_clk_get(dev->dev, "mod"); if (IS_ERR(dev->mod_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n"); + dev_err(dev->dev, "Failed to get MOD clock\n"); ret = PTR_ERR(dev->mod_clk); goto err_sram; @@ -212,7 +212,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->ram_clk = devm_clk_get(dev->dev, "ram"); if (IS_ERR(dev->ram_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n"); + dev_err(dev->dev, "Failed to get RAM clock\n"); ret = PTR_ERR(dev->ram_clk); goto err_sram; @@ -220,7 +220,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->rstc = devm_reset_control_get(dev->dev, NULL); if (IS_ERR(dev->rstc)) { - v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n"); + dev_err(dev->dev, "Failed to get reset control\n"); ret = PTR_ERR(dev->rstc); goto err_sram; @@ -229,7 +229,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(dev->dev, res); if (IS_ERR(dev->base)) { - v4l2_err(&dev->v4l2_dev, "Failed to map registers\n"); + dev_err(dev->dev, "Failed to map registers\n"); ret = PTR_ERR(dev->base); goto err_sram; @@ -237,35 +237,35 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n"); + dev_err(dev->dev, "Failed to set clock rate\n"); goto err_sram; } ret = clk_prepare_enable(dev->ahb_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n"); + dev_err(dev->dev, "Failed to enable AHB clock\n"); goto err_sram; } ret = clk_prepare_enable(dev->mod_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n"); + dev_err(dev->dev, "Failed to enable MOD clock\n"); goto err_ahb_clk; } ret = clk_prepare_enable(dev->ram_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n"); + dev_err(dev->dev, "Failed to enable RAM clock\n"); goto err_mod_clk; } ret = reset_control_reset(dev->rstc); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n"); + dev_err(dev->dev, "Failed to apply reset\n"); goto err_ram_clk; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 9abd39cae38c..13c34927bad5 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -82,7 +82,10 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t fwd_luma_addr, fwd_chroma_addr; dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *vq; const u8 *matrix; + int forward_idx; + int backward_idx; unsigned int i; u32 reg; @@ -157,22 +160,18 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) /* Forward and backward prediction reference buffers. */ - fwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 0); - fwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 1); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + forward_idx = vb2_find_timestamp(vq, slice_params->forward_ref_ts, 0); + fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); + fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - bwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 0); - bwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 1); + backward_idx = vb2_find_timestamp(vq, slice_params->backward_ref_ts, 0); + bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); + bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 8721b4a7d496..b47854b3bce4 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -282,8 +282,13 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, { struct cedrus_ctx *ctx = cedrus_file2ctx(file); struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_cap(file, priv, f); if (ret) return ret; @@ -299,8 +304,13 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_out(file, priv, f); if (ret) return ret; @@ -416,6 +426,14 @@ static void cedrus_buf_cleanup(struct vb2_buffer *vb) ctx->dst_bufs[vb->index] = NULL; } +static int cedrus_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + static int cedrus_buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -493,6 +511,7 @@ static struct vb2_ops cedrus_qops = { .buf_init = cedrus_buf_init, .buf_cleanup = cedrus_buf_cleanup, .buf_queue = cedrus_buf_queue, + .buf_out_validate = cedrus_buf_out_validate, .buf_request_complete = cedrus_buf_request_complete, .start_streaming = cedrus_start_streaming, .stop_streaming = cedrus_stop_streaming, diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h index 9bb3c21aa275..e84fb604a689 100644 --- a/drivers/staging/media/zoran/zoran.h +++ b/drivers/staging/media/zoran/zoran.h @@ -35,7 +35,7 @@ struct zoran_sync { unsigned long frame; /* number of buffer that has been free'd */ unsigned long length; /* number of code bytes in buffer (capture only) */ unsigned long seq; /* frame sequence number */ - struct timeval timestamp; /* timestamp */ + u64 ts; /* timestamp */ }; diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c index 94dadbba7cd5..ea10523194e8 100644 --- a/drivers/staging/media/zoran/zoran_card.c +++ b/drivers/staging/media/zoran/zoran_card.c @@ -1470,7 +1470,7 @@ static int __init zoran_init(void) v4l_nbufs = 2; if (v4l_nbufs > VIDEO_MAX_FRAME) v4l_nbufs = VIDEO_MAX_FRAME; - /* The user specfies the in KB, we want them in byte + /* The user specifies the in KB, we want them in byte * (and page aligned) */ v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); if (v4l_bufsize < 32768) diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c index 40adceebca7e..22b27632762d 100644 --- a/drivers/staging/media/zoran/zoran_device.c +++ b/drivers/staging/media/zoran/zoran_device.c @@ -612,7 +612,7 @@ zr36057_set_memgrab (struct zoran *zr, zr->v4l_memgrab_active = 0; zr->v4l_grab_frame = NO_GRAB_ACTIVE; - /* reenable grabbing to screen if it was running */ + /* re-enable grabbing to screen if it was running */ if (zr->v4l_overlay_active) { zr36057_overlay(zr, 1); } else { @@ -1151,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - v4l2_get_timestamp(&buffer->bs.timestamp); + buffer->bs.ts = ktime_get_ns(); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1389,7 +1389,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.ts = ktime_get_ns(); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 27c76e2eeb41..04f88f9d6bb4 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -1354,7 +1354,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; } @@ -1388,7 +1388,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); buf->bytesused = fh->buffers.buffer[num].bs.length; buf->flags |= V4L2_BUF_FLAG_DONE; } else { diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 611a6ee2943a..7c6cf41645eb 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1370,10 +1370,6 @@ static int vidioc_g_parm(struct file *file, void *priv, return 0; } -#define FRACT_CMP(a, OP, b) \ - ((u64)(a).numerator * (b).denominator OP \ - (u64)(b).numerator * (a).denominator) - static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { @@ -1387,8 +1383,8 @@ static int vidioc_s_parm(struct file *file, void *priv, /* tpf: {*, 0} resets timing; clip to [min, max]*/ tpf = tpf.denominator ? tpf : tpf_default; - tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; - tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf; dev->capture.timeperframe = tpf; parm->parm.capture.timeperframe = tpf; |