summaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/media/Kconfig1
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc0310.c14
-rw-r--r--drivers/staging/media/atomisp/i2c/gc0310.h1
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2680.h46
-rw-r--r--drivers/staging/media/atomisp/include/hmm/hmm.h3
-rw-r--r--drivers/staging/media/atomisp/include/hmm/hmm_bo.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c442
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.h17
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_common.h6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat.h14
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.c96
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.c535
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.h13
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_internal.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c615
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.h10
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c104
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h13
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h5
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c55
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c21
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h2
-rw-r--r--drivers/staging/media/atomisp/pci/hive_types.h2
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm.c20
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm_bo.c64
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_frame_public.h108
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_pipe.h3
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_pipe_public.h69
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c10
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c10
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c2
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c4
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c42
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c33
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c203
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h2
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c8
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css.c646
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_internal.h13
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_legacy.h1
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_shading.c19
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params.c17
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.c54
-rw-r--r--drivers/staging/media/deprecated/atmel/Kconfig47
-rw-r--r--drivers/staging/media/deprecated/atmel/Makefile8
-rw-r--r--drivers/staging/media/deprecated/atmel/TODO34
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-base.c2011
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-clk.c311
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-regs.h413
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc.h362
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c653
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c616
-rw-r--r--drivers/staging/media/deprecated/stkwebcam/Kconfig2
-rw-r--r--drivers/staging/media/imx/Kconfig17
-rw-r--r--drivers/staging/media/imx/Makefile3
-rw-r--r--drivers/staging/media/imx/TODO41
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c135
-rw-r--r--drivers/staging/media/imx/imx-media-fim.c57
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c33
-rw-r--r--drivers/staging/media/imx/imx-media.h1
-rw-r--r--drivers/staging/media/imx/imx7-media-csi.c2308
-rw-r--r--drivers/staging/media/ipu3/ipu3-v4l2.c57
-rw-r--r--drivers/staging/media/meson/vdec/codec_vp9.c10
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c2
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h18
-rw-r--r--drivers/staging/media/rkvdec/rkvdec-vp9.c3
-rw-r--r--drivers/staging/media/sunxi/Kconfig1
-rw-r--r--drivers/staging/media/sunxi/Makefile1
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.c112
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.h38
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_dec.c4
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h264.c120
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h265.c125
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.c18
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.h2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_regs.h18
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.c184
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.h2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_vp8.c2
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/Kconfig15
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/Makefile4
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/TODO.txt6
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c555
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h90
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c742
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h78
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c566
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h52
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c577
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h66
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h275
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h43
-rw-r--r--drivers/staging/media/tegra-video/csi.c4
-rw-r--r--drivers/staging/media/tegra-video/csi.h2
-rw-r--r--drivers/staging/media/tegra-video/vi.c2
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dtso (renamed from drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts)0
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433.txt6
-rw-r--r--drivers/staging/sm750fb/Kconfig1
-rw-r--r--drivers/staging/sm750fb/sm750.c3
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c4
-rw-r--r--drivers/staging/wlan-ng/prism2usb.c6
105 files changed, 8814 insertions, 5427 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index d4f03b203ae5..b79f93684c4f 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -51,6 +51,7 @@ menuconfig STAGING_MEDIA_DEPRECATED
If in doubt, say N here.
if STAGING_MEDIA_DEPRECATED
+source "drivers/staging/media/deprecated/atmel/Kconfig"
source "drivers/staging/media/deprecated/cpia2/Kconfig"
source "drivers/staging/media/deprecated/fsl-viu/Kconfig"
source "drivers/staging/media/deprecated/meye/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index a387692b84f2..54bbdd4b0d08 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/
obj-$(CONFIG_INTEL_ATOMISP) += atomisp/
obj-$(CONFIG_VIDEO_CPIA2) += deprecated/cpia2/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
index 783f1b88ebf2..87a634bf9ff5 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
@@ -786,8 +786,6 @@ static int gpio_ctrl(struct v4l2_subdev *sd, bool flag)
return ret;
}
-static int power_down(struct v4l2_subdev *sd);
-
static int power_up(struct v4l2_subdev *sd)
{
struct gc0310_device *dev = to_gc0310_sensor(sd);
@@ -800,6 +798,9 @@ static int power_up(struct v4l2_subdev *sd)
return -ENODEV;
}
+ if (dev->power_on)
+ return 0; /* Already on */
+
/* power control */
ret = power_ctrl(sd, 1);
if (ret)
@@ -820,6 +821,7 @@ static int power_up(struct v4l2_subdev *sd)
msleep(100);
+ dev->power_on = true;
return 0;
fail_gpio:
@@ -844,6 +846,9 @@ static int power_down(struct v4l2_subdev *sd)
return -ENODEV;
}
+ if (!dev->power_on)
+ return 0; /* Already off */
+
/* gpio ctrl */
ret = gpio_ctrl(sd, 0);
if (ret) {
@@ -861,6 +866,7 @@ static int power_down(struct v4l2_subdev *sd)
if (ret)
dev_err(&client->dev, "vprog failed.\n");
+ dev->power_on = false;
return ret;
}
@@ -935,6 +941,9 @@ static int gc0310_set_fmt(struct v4l2_subdev *sd,
return 0;
}
+ /* s_power has not been called yet for std v4l2 clients (camorama) */
+ power_up(sd);
+
dev_dbg(&client->dev, "%s: before gc0310_write_reg_array %s\n",
__func__, dev->res->desc);
ret = startup(sd);
@@ -1073,6 +1082,7 @@ static int gc0310_s_config(struct v4l2_subdev *sd,
* as first power on by board may not fulfill the
* power on sequqence needed by the module
*/
+ dev->power_on = true; /* force power_down() to run */
ret = power_down(sd);
if (ret) {
dev_err(&client->dev, "gc0310 power-off err.\n");
diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h
index db643ebc3909..4b9ce681bd93 100644
--- a/drivers/staging/media/atomisp/i2c/gc0310.h
+++ b/drivers/staging/media/atomisp/i2c/gc0310.h
@@ -152,6 +152,7 @@ struct gc0310_device {
int vt_pix_clk_freq_mhz;
struct gc0310_resolution *res;
u8 type;
+ bool power_on;
};
enum gc0310_tok_type {
diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h
index 4e351196fe34..7ab337b859ad 100644
--- a/drivers/staging/media/atomisp/i2c/ov2680.h
+++ b/drivers/staging/media/atomisp/i2c/ov2680.h
@@ -485,19 +485,19 @@ static struct ov2680_reg const ov2680_720x592_30fps[] = {
static struct ov2680_reg const ov2680_800x600_30fps[] = {
{0x3086, 0x01},
{0x370a, 0x23},
- {0x3801, 0x00},
+ {0x3801, 0x00}, /* hstart 0 */
{0x3802, 0x00},
- {0x3803, 0x00},
+ {0x3803, 0x00}, /* vstart 0 */
{0x3804, 0x06},
- {0x3805, 0x4f},
+ {0x3805, 0x4f}, /* hend 1615 */
{0x3806, 0x04},
- {0x3807, 0xbf},
+ {0x3807, 0xbf}, /* vend 1215 */
{0x3808, 0x03},
- {0x3809, 0x20},
+ {0x3809, 0x20}, /* hsize 800 */
{0x380a, 0x02},
- {0x380b, 0x58},
+ {0x380b, 0x58}, /* vsize 600 */
{0x380c, 0x06},
- {0x380d, 0xac},
+ {0x380d, 0xac}, /* htotal 1708 */
{0x3810, 0x00},
{0x3811, 0x00},
{0x3812, 0x00},
@@ -524,19 +524,19 @@ static struct ov2680_reg const ov2680_800x600_30fps[] = {
static struct ov2680_reg const ov2680_720p_30fps[] = {
{0x3086, 0x00},
{0x370a, 0x21},
- {0x3801, 0xa0},
+ {0x3801, 0xa0}, /* hstart 160 */
{0x3802, 0x00},
- {0x3803, 0xf2},
+ {0x3803, 0xf2}, /* vstart 242 */
{0x3804, 0x05},
- {0x3805, 0xbf},
+ {0x3805, 0xbf}, /* hend 1471 */
{0x3806, 0x03},
- {0x3807, 0xdd},
+ {0x3807, 0xdd}, /* vend 989 */
{0x3808, 0x05},
- {0x3809, 0x10},
+ {0x3809, 0x10}, /* hsize 1296 */
{0x380a, 0x02},
- {0x380b, 0xe0},
+ {0x380b, 0xe0}, /* vsize 736 */
{0x380c, 0x06},
- {0x380d, 0xa8},
+ {0x380d, 0xa8}, /* htotal 1704 */
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
@@ -563,19 +563,19 @@ static struct ov2680_reg const ov2680_720p_30fps[] = {
static struct ov2680_reg const ov2680_1296x976_30fps[] = {
{0x3086, 0x00},
{0x370a, 0x21},
- {0x3801, 0xa0},
+ {0x3801, 0xa0}, /* hstart 160 */
{0x3802, 0x00},
- {0x3803, 0x78},
+ {0x3803, 0x78}, /* vstart 120 */
{0x3804, 0x05},
- {0x3805, 0xbf},
+ {0x3805, 0xbf}, /* hend 1471 */
{0x3806, 0x04},
- {0x3807, 0x57},
+ {0x3807, 0x57}, /* vend 1111 */
{0x3808, 0x05},
- {0x3809, 0x10},
+ {0x3809, 0x10}, /* hsize 1296 */
{0x380a, 0x03},
- {0x380b, 0xd0},
+ {0x380b, 0xd0}, /* vsize 976 */
{0x380c, 0x06},
- {0x380d, 0xa8},
+ {0x380d, 0xa8}, /* htotal 1704 */
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
@@ -820,8 +820,8 @@ static struct ov2680_resolution ov2680_res_preview[] = {
.regs = ov2680_1296x976_30fps,
},
{
- .width = 1280,
- .height = 720,
+ .width = 1296,
+ .height = 736,
.fps = 60,
.pix_clk_freq = 66,
.pixels_per_line = 1698,//1704,
diff --git a/drivers/staging/media/atomisp/include/hmm/hmm.h b/drivers/staging/media/atomisp/include/hmm/hmm.h
index c0384bb0a762..2bc323b34f89 100644
--- a/drivers/staging/media/atomisp/include/hmm/hmm.h
+++ b/drivers/staging/media/atomisp/include/hmm/hmm.h
@@ -37,7 +37,8 @@ int hmm_init(void);
void hmm_cleanup(void);
ia_css_ptr hmm_alloc(size_t bytes);
-ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr);
+ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr);
+
void hmm_free(ia_css_ptr ptr);
int hmm_load(ia_css_ptr virt, void *data, unsigned int bytes);
int hmm_store(ia_css_ptr virt, const void *data, unsigned int bytes);
diff --git a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h
index c5cbae1d9cf9..b4c03e0ca9c0 100644
--- a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h
+++ b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h
@@ -73,7 +73,7 @@
enum hmm_bo_type {
HMM_BO_PRIVATE,
- HMM_BO_USER,
+ HMM_BO_VMALLOC,
HMM_BO_LAST,
};
@@ -207,7 +207,7 @@ int hmm_bo_allocated(struct hmm_buffer_object *bo);
*/
int hmm_bo_alloc_pages(struct hmm_buffer_object *bo,
enum hmm_bo_type type,
- const void __user *userptr);
+ void *vmalloc_addr);
void hmm_bo_free_pages(struct hmm_buffer_object *bo);
int hmm_bo_page_allocated(struct hmm_buffer_object *bo);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index c72d0e344671..d8c7e7367386 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -30,7 +30,6 @@
#include <asm/iosf_mbi.h>
#include <media/v4l2-event.h>
-#include <media/videobuf-vmalloc.h>
#define CREATE_TRACE_POINTS
#include "atomisp_trace_event.h"
@@ -208,11 +207,6 @@ int atomisp_freq_scaling(struct atomisp_device *isp,
int i, ret;
unsigned short fps = 0;
- if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP) {
- dev_err(isp->dev, "DFS cannot proceed due to no power.\n");
- return -EINVAL;
- }
-
if ((pdev->device & ATOMISP_PCI_DEVICE_SOC_MASK) ==
ATOMISP_PCI_DEVICE_SOC_CHT && ATOMISP_USE_YUVPP(asd))
isp->dfs = &dfs_config_cht_soc;
@@ -308,24 +302,16 @@ int atomisp_reset(struct atomisp_device *isp)
int ret = 0;
dev_dbg(isp->dev, "%s\n", __func__);
- atomisp_css_suspend(isp);
- ret = atomisp_runtime_suspend(isp->dev);
+
+ ret = atomisp_power_off(isp->dev);
if (ret < 0)
- dev_err(isp->dev, "atomisp_runtime_suspend failed, %d\n", ret);
- ret = atomisp_mrfld_power_down(isp);
+ dev_err(isp->dev, "atomisp_power_off failed, %d\n", ret);
+
+ ret = atomisp_power_on(isp->dev);
if (ret < 0) {
- dev_err(isp->dev, "can not disable ISP power\n");
- } else {
- ret = atomisp_mrfld_power_up(isp);
- if (ret < 0)
- dev_err(isp->dev, "can not enable ISP power\n");
- ret = atomisp_runtime_resume(isp->dev);
- if (ret < 0)
- dev_err(isp->dev, "atomisp_runtime_resume failed, %d\n", ret);
- }
- ret = atomisp_css_resume(isp);
- if (ret)
+ dev_err(isp->dev, "atomisp_power_on failed, %d\n", ret);
isp->isp_fatal_error = true;
+ }
return ret;
}
@@ -518,8 +504,8 @@ irqreturn_t atomisp_isr(int irq, void *dev)
int err;
spin_lock_irqsave(&isp->lock, flags);
- if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP ||
- !isp->css_initialized) {
+
+ if (!isp->css_initialized) {
spin_unlock_irqrestore(&isp->lock, flags);
return IRQ_HANDLED;
}
@@ -634,25 +620,6 @@ void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd)
memset(asd->metadata_bufs_in_css[i], 0,
sizeof(asd->metadata_bufs_in_css[i]));
asd->dis_bufs_in_css = 0;
- asd->video_out_capture.buffers_in_css = 0;
- asd->video_out_vf.buffers_in_css = 0;
- asd->video_out_preview.buffers_in_css = 0;
- asd->video_out_video_capture.buffers_in_css = 0;
-}
-
-/* ISP2400 */
-bool atomisp_buffers_queued(struct atomisp_sub_device *asd)
-{
- return asd->video_out_capture.buffers_in_css ||
- asd->video_out_vf.buffers_in_css ||
- asd->video_out_preview.buffers_in_css ||
- asd->video_out_video_capture.buffers_in_css;
-}
-
-/* ISP2401 */
-bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe)
-{
- return pipe->buffers_in_css ? true : false;
}
/* 0x100000 is the start of dmem inside SP */
@@ -681,57 +648,68 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr,
} while (--size32);
}
-static struct videobuf_buffer *atomisp_css_frame_to_vbuf(
- struct atomisp_video_pipe *pipe, struct ia_css_frame *frame)
+int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe)
{
- struct videobuf_vmalloc_memory *vm_mem;
- struct ia_css_frame *handle;
- int i;
+ unsigned long irqflags;
+ struct list_head *pos;
+ int buffers_in_css = 0;
- for (i = 0; pipe->capq.bufs[i]; i++) {
- vm_mem = pipe->capq.bufs[i]->priv;
- handle = vm_mem->vaddr;
- if (handle && handle->data == frame->data)
- return pipe->capq.bufs[i];
- }
+ spin_lock_irqsave(&pipe->irq_lock, irqflags);
- return NULL;
+ list_for_each(pos, &pipe->buffers_in_css)
+ buffers_in_css++;
+
+ spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+
+ return buffers_in_css;
}
-static void atomisp_flush_video_pipe(struct atomisp_sub_device *asd,
- struct atomisp_video_pipe *pipe)
+void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state)
{
+ struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf);
+
+ lockdep_assert_held(&pipe->irq_lock);
+
+ frame->vb.vb2_buf.timestamp = ktime_get_ns();
+ frame->vb.field = pipe->pix.field;
+ frame->vb.sequence = atomic_read(&pipe->asd->sequence);
+ list_del(&frame->queue);
+ if (state == VB2_BUF_STATE_DONE)
+ vb2_set_plane_payload(&frame->vb.vb2_buf, 0, pipe->pix.sizeimage);
+ vb2_buffer_done(&frame->vb.vb2_buf, state);
+}
+
+void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames)
+{
+ struct ia_css_frame *frame, *_frame;
unsigned long irqflags;
- int i;
- if (!pipe->users)
- return;
+ spin_lock_irqsave(&pipe->irq_lock, irqflags);
- for (i = 0; pipe->capq.bufs[i]; i++) {
- spin_lock_irqsave(&pipe->irq_lock, irqflags);
- if (pipe->capq.bufs[i]->state == VIDEOBUF_ACTIVE ||
- pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) {
- pipe->capq.bufs[i]->ts = ktime_get_ns();
- pipe->capq.bufs[i]->field_count =
- atomic_read(&asd->sequence) << 1;
- dev_dbg(asd->isp->dev, "release buffers on device %s\n",
- pipe->vdev.name);
- if (pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED)
- list_del_init(&pipe->capq.bufs[i]->queue);
- pipe->capq.bufs[i]->state = VIDEOBUF_ERROR;
- wake_up(&pipe->capq.bufs[i]->done);
- }
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+ list_for_each_entry_safe(frame, _frame, &pipe->buffers_in_css, queue) {
+ if (warn_on_css_frames)
+ dev_warn(pipe->isp->dev, "Warning: CSS frames queued on flush\n");
+ atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
+ }
+
+ list_for_each_entry_safe(frame, _frame, &pipe->activeq, queue)
+ atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
+
+ list_for_each_entry_safe(frame, _frame, &pipe->buffers_waiting_for_param, queue) {
+ pipe->frame_request_config_id[frame->vb.vb2_buf.index] = 0;
+ atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
}
+
+ spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
}
/* Returns queued buffers back to video-core */
void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd)
{
- atomisp_flush_video_pipe(asd, &asd->video_out_capture);
- atomisp_flush_video_pipe(asd, &asd->video_out_vf);
- atomisp_flush_video_pipe(asd, &asd->video_out_preview);
- atomisp_flush_video_pipe(asd, &asd->video_out_video_capture);
+ atomisp_flush_video_pipe(&asd->video_out_capture, false);
+ atomisp_flush_video_pipe(&asd->video_out_vf, false);
+ atomisp_flush_video_pipe(&asd->video_out_preview, false);
+ atomisp_flush_video_pipe(&asd->video_out_video_capture, false);
}
/* clean out the parameters that did not apply */
@@ -763,93 +741,6 @@ static void atomisp_recover_params_queue(struct atomisp_video_pipe *pipe)
atomisp_handle_parameter_and_buffer(pipe);
}
-/* find atomisp_video_pipe with css pipe id, buffer type and atomisp run_mode */
-static struct atomisp_video_pipe *__atomisp_get_pipe(
- struct atomisp_sub_device *asd,
- enum atomisp_input_stream_id stream_id,
- enum ia_css_pipe_id css_pipe_id,
- enum ia_css_buffer_type buf_type)
-{
- /* video is same in online as in continuouscapture mode */
- if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) {
- /*
- * Disable vf_pp and run CSS in still capture mode. In this
- * mode, CSS does not cause extra latency with buffering, but
- * scaling is not available.
- */
- return &asd->video_out_capture;
- } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) {
- /*
- * Disable vf_pp and run CSS in video mode. This allows using
- * ISP scaling but it has one frame delay due to CSS internal
- * buffering.
- */
- return &asd->video_out_video_capture;
- } else if (css_pipe_id == IA_CSS_PIPE_ID_YUVPP) {
- /*
- * to SOC camera, yuvpp pipe is run for capture/video/SDV/ZSL.
- */
- if (asd->continuous_mode->val) {
- if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
- /* SDV case */
- switch (buf_type) {
- case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME:
- return &asd->video_out_video_capture;
- case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME:
- return &asd->video_out_preview;
- case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
- return &asd->video_out_capture;
- default:
- return &asd->video_out_vf;
- }
- } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) {
- /* ZSL case */
- switch (buf_type) {
- case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME:
- return &asd->video_out_preview;
- case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
- return &asd->video_out_capture;
- default:
- return &asd->video_out_vf;
- }
- }
- } else if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) {
- switch (asd->run_mode->val) {
- case ATOMISP_RUN_MODE_VIDEO:
- return &asd->video_out_video_capture;
- case ATOMISP_RUN_MODE_PREVIEW:
- return &asd->video_out_preview;
- default:
- return &asd->video_out_capture;
- }
- } else if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) {
- if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO)
- return &asd->video_out_preview;
- else
- return &asd->video_out_vf;
- }
- } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
- /* For online video or SDV video pipe. */
- if (css_pipe_id == IA_CSS_PIPE_ID_VIDEO ||
- css_pipe_id == IA_CSS_PIPE_ID_COPY ||
- css_pipe_id == IA_CSS_PIPE_ID_YUVPP) {
- if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
- return &asd->video_out_video_capture;
- return &asd->video_out_preview;
- }
- } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) {
- /* For online preview or ZSL preview pipe. */
- if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW ||
- css_pipe_id == IA_CSS_PIPE_ID_COPY ||
- css_pipe_id == IA_CSS_PIPE_ID_YUVPP)
- return &asd->video_out_preview;
- }
- /* For capture pipe. */
- if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME)
- return &asd->video_out_vf;
- return &asd->video_out_capture;
-}
-
enum atomisp_metadata_type
atomisp_get_metadata_type(struct atomisp_sub_device *asd,
enum ia_css_pipe_id pipe_id)
@@ -868,11 +759,9 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
enum ia_css_pipe_id css_pipe_id,
bool q_buffers, enum atomisp_input_stream_id stream_id)
{
- struct videobuf_buffer *vb = NULL;
struct atomisp_video_pipe *pipe = NULL;
struct atomisp_css_buffer buffer;
bool requeue = false;
- int err;
unsigned long irqflags;
struct ia_css_frame *frame = NULL;
struct atomisp_s3a_buf *s3a_buf = NULL, *_s3a_buf_tmp, *s3a_iter;
@@ -881,6 +770,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
enum atomisp_metadata_type md_type;
struct atomisp_device *isp = asd->isp;
struct v4l2_control ctrl;
+ int i, err;
lockdep_assert_held(&isp->mutex);
@@ -908,13 +798,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
return;
}
- /* need to know the atomisp pipe for frame buffers */
- pipe = __atomisp_get_pipe(asd, stream_id, css_pipe_id, buf_type);
- if (!pipe) {
- dev_err(isp->dev, "error getting atomisp pipe\n");
- return;
- }
-
switch (buf_type) {
case IA_CSS_BUFFER_TYPE_3A_STATISTICS:
list_for_each_entry_safe(s3a_iter, _s3a_buf_tmp,
@@ -989,7 +872,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
break;
case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME:
case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME:
- pipe->buffers_in_css--;
frame = buffer.css_buffer.data.frame;
if (!frame) {
WARN_ON(1);
@@ -998,6 +880,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
if (!frame->valid)
error = true;
+ pipe = vb_to_pipe(&frame->vb.vb2_buf);
+
/* FIXME:
* YUVPP doesn't set postview exp_id correctlly in SDV mode.
* This is a WORKAROUND to set exp_id. see HSDES-1503911606.
@@ -1022,10 +906,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
dev_dbg(isp->dev, "%s thumb no flash in this frame\n",
__func__);
}
- vb = atomisp_css_frame_to_vbuf(pipe, frame);
- WARN_ON(!vb);
- if (vb)
- pipe->frame_config_id[vb->i] = frame->isp_config_id;
+ pipe->frame_config_id[frame->vb.vb2_buf.index] = frame->isp_config_id;
if (css_pipe_id == IA_CSS_PIPE_ID_CAPTURE &&
asd->pending_capture_request > 0) {
err = atomisp_css_offline_capture_configure(asd,
@@ -1041,7 +922,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
break;
case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME:
- pipe->buffers_in_css--;
frame = buffer.css_buffer.data.frame;
if (!frame) {
WARN_ON(1);
@@ -1051,6 +931,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
if (!frame->valid)
error = true;
+ pipe = vb_to_pipe(&frame->vb.vb2_buf);
+
/* FIXME:
* YUVPP doesn't set preview exp_id correctlly in ZSL mode.
* This is a WORKAROUND to set exp_id. see HSDES-1503911606.
@@ -1062,72 +944,53 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
dev_dbg(isp->dev, "%s: main frame with exp_id %d is ready\n",
__func__, frame->exp_id);
- vb = atomisp_css_frame_to_vbuf(pipe, frame);
- if (!vb) {
- WARN_ON(1);
- break;
- }
+
+ i = frame->vb.vb2_buf.index;
/* free the parameters */
- if (pipe->frame_params[vb->i]) {
- if (asd->params.dvs_6axis ==
- pipe->frame_params[vb->i]->params.dvs_6axis)
+ if (pipe->frame_params[i]) {
+ if (asd->params.dvs_6axis == pipe->frame_params[i]->params.dvs_6axis)
asd->params.dvs_6axis = NULL;
- atomisp_free_css_parameters(
- &pipe->frame_params[vb->i]->params);
- kvfree(pipe->frame_params[vb->i]);
- pipe->frame_params[vb->i] = NULL;
+ atomisp_free_css_parameters(&pipe->frame_params[i]->params);
+ kvfree(pipe->frame_params[i]);
+ pipe->frame_params[i] = NULL;
}
- pipe->frame_config_id[vb->i] = frame->isp_config_id;
+ pipe->frame_config_id[i] = frame->isp_config_id;
ctrl.id = V4L2_CID_FLASH_MODE;
if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) {
- if (frame->flash_state
- == IA_CSS_FRAME_FLASH_STATE_PARTIAL) {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_FLASH_PARTIAL;
- dev_dbg(isp->dev, "%s partially flashed\n",
- __func__);
- } else if (frame->flash_state
- == IA_CSS_FRAME_FLASH_STATE_FULL) {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
+ if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_PARTIAL) {
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_PARTIAL;
+ dev_dbg(isp->dev, "%s partially flashed\n", __func__);
+ } else if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_FULL) {
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
asd->params.num_flash_frames--;
- dev_dbg(isp->dev, "%s completely flashed\n",
- __func__);
+ dev_dbg(isp->dev, "%s completely flashed\n", __func__);
} else {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_OK;
- dev_dbg(isp->dev,
- "%s no flash in this frame\n",
- __func__);
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
+ dev_dbg(isp->dev, "%s no flash in this frame\n", __func__);
}
/* Check if flashing sequence is done */
- if (asd->frame_status[vb->i] ==
- ATOMISP_FRAME_STATUS_FLASH_EXPOSED)
+ if (asd->frame_status[i] == ATOMISP_FRAME_STATUS_FLASH_EXPOSED)
asd->params.flash_state = ATOMISP_FLASH_DONE;
} else if (isp->flash) {
- if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) ==
- 0 && ctrl.value == ATOMISP_FLASH_MODE_TORCH) {
+ if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 &&
+ ctrl.value == ATOMISP_FLASH_MODE_TORCH) {
ctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY;
- if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl)
- == 0 && ctrl.value > 0) {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
- } else {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_OK;
- }
+ if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 &&
+ ctrl.value > 0)
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED;
+ else
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
} else {
- asd->frame_status[vb->i] =
- ATOMISP_FRAME_STATUS_OK;
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
}
} else {
- asd->frame_status[vb->i] = ATOMISP_FRAME_STATUS_OK;
+ asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK;
}
- asd->params.last_frame_status = asd->frame_status[vb->i];
+ asd->params.last_frame_status = asd->frame_status[i];
if (asd->continuous_mode->val) {
if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW ||
@@ -1193,20 +1056,10 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
default:
break;
}
- if (vb) {
- vb->ts = ktime_get_ns();
- vb->field_count = atomic_read(&asd->sequence) << 1;
- /*mark videobuffer done for dequeue*/
+ if (frame) {
spin_lock_irqsave(&pipe->irq_lock, irqflags);
- vb->state = !error ? VIDEOBUF_DONE : VIDEOBUF_ERROR;
+ atomisp_buffer_done(frame, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
-
- /*
- * Frame capture done, wake up any process block on
- * current active buffer
- * possibly hold by videobuf_dqbuf()
- */
- wake_up(&vb->done);
}
/*
@@ -3288,7 +3141,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd,
if (!IS_ISP2401) {
if (sizeof(*cur) != sizeof(coefs->grid) ||
memcmp(&coefs->grid, cur, sizeof(coefs->grid))) {
- dev_err(asd->isp->dev, "dvs grid mis-match!\n");
+ dev_err(asd->isp->dev, "dvs grid mismatch!\n");
/* If the grid info in the argument differs from the current
grid info, we tell the caller to reset the grid size and
try again. */
@@ -3344,7 +3197,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd,
if (sizeof(*cur) != sizeof(dvs2_coefs.grid) ||
memcmp(&dvs2_coefs.grid, cur, sizeof(dvs2_coefs.grid))) {
- dev_err(asd->isp->dev, "dvs grid mis-match!\n");
+ dev_err(asd->isp->dev, "dvs grid mismatch!\n");
/* If the grid info in the argument differs from the current
grid info, we tell the caller to reset the grid size and
try again. */
@@ -3676,6 +3529,18 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param)
}
}
+static void atomisp_move_frame_to_activeq(struct ia_css_frame *frame,
+ struct atomisp_css_params_with_list *param)
+{
+ struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf);
+ unsigned long irqflags;
+
+ pipe->frame_params[frame->vb.vb2_buf.index] = param;
+ spin_lock_irqsave(&pipe->irq_lock, irqflags);
+ list_move_tail(&frame->queue, &pipe->activeq);
+ spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+}
+
/*
* Check parameter queue list and buffer queue list to find out if matched items
* and then set parameter to CSS and enqueue buffer to CSS.
@@ -3686,13 +3551,10 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param)
void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe)
{
struct atomisp_sub_device *asd = pipe->asd;
- struct videobuf_buffer *vb = NULL, *vb_tmp;
+ struct ia_css_frame *frame = NULL, *frame_tmp;
struct atomisp_css_params_with_list *param = NULL, *param_tmp;
- struct videobuf_vmalloc_memory *vm_mem = NULL;
- unsigned long irqflags;
bool need_to_enqueue_buffer = false;
-
- lockdep_assert_held(&asd->isp->mutex);
+ int i;
if (!asd) {
dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n",
@@ -3700,6 +3562,8 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe)
return;
}
+ lockdep_assert_held(&asd->isp->mutex);
+
if (atomisp_is_vf_pipe(pipe))
return;
@@ -3714,44 +3578,32 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe)
list_empty(&pipe->buffers_waiting_for_param))
return;
- list_for_each_entry_safe(vb, vb_tmp,
+ list_for_each_entry_safe(frame, frame_tmp,
&pipe->buffers_waiting_for_param, queue) {
- if (pipe->frame_request_config_id[vb->i]) {
+ i = frame->vb.vb2_buf.index;
+ if (pipe->frame_request_config_id[i]) {
list_for_each_entry_safe(param, param_tmp,
&pipe->per_frame_params, list) {
- if (pipe->frame_request_config_id[vb->i] !=
- param->params.isp_config_id)
+ if (pipe->frame_request_config_id[i] != param->params.isp_config_id)
continue;
list_del(&param->list);
- list_del(&vb->queue);
+
/*
* clear the request config id as the buffer
* will be handled and enqueued into CSS soon
*/
- pipe->frame_request_config_id[vb->i] = 0;
- pipe->frame_params[vb->i] = param;
- vm_mem = vb->priv;
- BUG_ON(!vm_mem);
+ pipe->frame_request_config_id[i] = 0;
+ atomisp_move_frame_to_activeq(frame, param);
+ need_to_enqueue_buffer = true;
break;
}
- if (vm_mem) {
- spin_lock_irqsave(&pipe->irq_lock, irqflags);
- list_add_tail(&vb->queue, &pipe->activeq);
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
- vm_mem = NULL;
- need_to_enqueue_buffer = true;
- } else {
- /* The is the end, stop further loop */
+ /* If this is the end, stop further loop */
+ if (list_entry_is_head(param, &pipe->per_frame_params, list))
break;
- }
} else {
- list_del(&vb->queue);
- pipe->frame_params[vb->i] = NULL;
- spin_lock_irqsave(&pipe->irq_lock, irqflags);
- list_add_tail(&vb->queue, &pipe->activeq);
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+ atomisp_move_frame_to_activeq(frame, NULL);
need_to_enqueue_buffer = true;
}
}
@@ -3774,14 +3626,14 @@ int atomisp_set_parameters(struct video_device *vdev,
struct atomisp_css_params *css_param = &asd->params.css_param;
int ret;
- lockdep_assert_held(&asd->isp->mutex);
-
if (!asd) {
dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n",
__func__, vdev->name);
return -EINVAL;
}
+ lockdep_assert_held(&asd->isp->mutex);
+
if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
dev_err(asd->isp->dev, "%s: internal error!\n", __func__);
return -EINVAL;
@@ -5140,9 +4992,8 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev,
return css_input_resolution_changed(asd, ffmt);
}
-int atomisp_set_fmt(struct file *file, void *unused, struct v4l2_format *f)
+int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f)
{
- struct video_device *vdev = video_devdata(file);
struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
struct atomisp_sub_device *asd = pipe->asd;
@@ -5558,8 +5409,6 @@ done:
f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width *
pipe->pix.height * 2);
- pipe->capq.field = f->fmt.pix.field;
-
/*
* If in video 480P case, no GFX throttle
*/
@@ -5643,51 +5492,6 @@ out:
return ret;
}
-/*Turn off ISP dphy */
-int atomisp_ospm_dphy_down(struct atomisp_device *isp)
-{
- struct pci_dev *pdev = to_pci_dev(isp->dev);
- unsigned long flags;
- u32 reg;
-
- dev_dbg(isp->dev, "%s\n", __func__);
-
- /* if ISP timeout, we can force powerdown */
- if (isp->isp_timeout)
- goto done;
-
- if (!atomisp_dev_users(isp))
- goto done;
-
- spin_lock_irqsave(&isp->lock, flags);
- isp->sw_contex.power_state = ATOM_ISP_POWER_DOWN;
- spin_unlock_irqrestore(&isp->lock, flags);
-done:
- /*
- * MRFLD IUNIT DPHY is located in an always-power-on island
- * MRFLD HW design need all CSI ports are disabled before
- * powering down the IUNIT.
- */
- pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, &reg);
- reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK;
- pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg);
- return 0;
-}
-
-/*Turn on ISP dphy */
-int atomisp_ospm_dphy_up(struct atomisp_device *isp)
-{
- unsigned long flags;
-
- dev_dbg(isp->dev, "%s\n", __func__);
-
- spin_lock_irqsave(&isp->lock, flags);
- isp->sw_contex.power_state = ATOM_ISP_POWER_UP;
- spin_unlock_irqrestore(&isp->lock, flags);
-
- return 0;
-}
-
int atomisp_exif_makernote(struct atomisp_sub_device *asd,
struct atomisp_makernote_info *config)
{
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
index c9f92f1326b6..b8911491581a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
@@ -55,12 +55,11 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr,
struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd);
struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev);
int atomisp_reset(struct atomisp_device *isp);
+int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe);
+void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state);
+void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames);
void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd);
void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd);
-/* ISP2400 */
-bool atomisp_buffers_queued(struct atomisp_sub_device *asd);
-/* ISP2401 */
-bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe);
/* Interrupt functions */
void atomisp_msi_irq_init(struct atomisp_device *isp);
@@ -266,7 +265,7 @@ int atomisp_get_sensor_mode_data(struct atomisp_sub_device *asd,
int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f,
bool *res_overflow);
-int atomisp_set_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f);
int atomisp_set_shading_table(struct atomisp_sub_device *asd,
struct atomisp_shading_table *shading_table);
@@ -274,8 +273,6 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd,
int atomisp_offline_capture_configure(struct atomisp_sub_device *asd,
struct atomisp_cont_capture_conf *cvf_config);
-int atomisp_ospm_dphy_down(struct atomisp_device *isp);
-int atomisp_ospm_dphy_up(struct atomisp_device *isp);
int atomisp_exif_makernote(struct atomisp_sub_device *asd,
struct atomisp_makernote_info *config);
@@ -342,8 +339,6 @@ int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event);
int atomisp_get_invalid_frame_num(struct video_device *vdev,
int *invalid_frame_num);
-int atomisp_mrfld_power_up(struct atomisp_device *isp);
-int atomisp_mrfld_power_down(struct atomisp_device *isp);
-int atomisp_runtime_suspend(struct device *dev);
-int atomisp_runtime_resume(struct device *dev);
+int atomisp_power_off(struct device *dev);
+int atomisp_power_on(struct device *dev);
#endif /* __ATOMISP_CMD_H__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_common.h b/drivers/staging/media/atomisp/pci/atomisp_common.h
index b29874f2bc0f..07c38e487a66 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_common.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_common.h
@@ -25,7 +25,7 @@
#include <linux/v4l2-mediabus.h>
-#include <media/videobuf-core.h>
+#include <media/videobuf2-v4l2.h>
#include "atomisp_compat.h"
@@ -64,8 +64,4 @@ struct atomisp_fmt {
u32 bayer_order;
};
-struct atomisp_buffer {
- struct videobuf_buffer vb;
-};
-
#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat.h b/drivers/staging/media/atomisp/pci/atomisp_compat.h
index a6d85d0f9ae5..7316eb9f974a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat.h
@@ -22,7 +22,6 @@
#include "atomisp_compat_css20.h"
#include "../../include/linux/atomisp.h"
-#include <media/videobuf-vmalloc.h>
struct atomisp_device;
struct atomisp_sub_device;
@@ -42,10 +41,6 @@ int atomisp_css_init(struct atomisp_device *isp);
void atomisp_css_uninit(struct atomisp_device *isp);
-void atomisp_css_suspend(struct atomisp_device *isp);
-
-int atomisp_css_resume(struct atomisp_device *isp);
-
void atomisp_css_init_struct(struct atomisp_sub_device *asd);
int atomisp_css_irq_translate(struct atomisp_device *isp,
@@ -61,7 +56,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp,
enum ia_css_irq_info info, bool enable);
int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd,
- struct videobuf_vmalloc_memory *vm_mem,
+ struct ia_css_frame *frame,
enum atomisp_input_stream_id stream_id,
enum ia_css_buffer_type css_buf_type,
enum ia_css_pipe_id css_pipe_id);
@@ -258,13 +253,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd,
unsigned int padded_width,
enum ia_css_frame_format format);
-int atomisp_css_yuvpp_configure_viewfinder(
- struct atomisp_sub_device *asd,
- unsigned int stream_index,
- unsigned int width, unsigned int height,
- unsigned int min_width,
- enum ia_css_frame_format format);
-
int atomisp_css_yuvpp_get_output_frame_info(
struct atomisp_sub_device *asd,
unsigned int stream_index,
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
index fdc05548d972..61e2e63a0ef1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
@@ -237,18 +237,6 @@ static void __dump_pipe_config(struct atomisp_sub_device *asd,
"pipe_config.isp_pipe_version:%d.\n",
p_config->isp_pipe_version);
dev_dbg(isp->dev,
- "pipe_config.acc_extension=%p.\n",
- p_config->acc_extension);
- dev_dbg(isp->dev,
- "pipe_config.acc_stages=%p.\n",
- p_config->acc_stages);
- dev_dbg(isp->dev,
- "pipe_config.num_acc_stages=%d.\n",
- p_config->num_acc_stages);
- dev_dbg(isp->dev,
- "pipe_config.acc_num_execs=%d.\n",
- p_config->acc_num_execs);
- dev_dbg(isp->dev,
"pipe_config.default_capture_config.capture_mode=%d.\n",
p_config->default_capture_config.mode);
dev_dbg(isp->dev,
@@ -629,10 +617,6 @@ static void __apply_additional_pipe_config(
else
stream_env->pipe_configs[pipe_id].enable_dz = false;
break;
- case IA_CSS_PIPE_ID_ACC:
- stream_env->pipe_configs[pipe_id].mode = IA_CSS_PIPE_MODE_ACC;
- stream_env->pipe_configs[pipe_id].enable_dz = false;
- break;
default:
break;
}
@@ -644,7 +628,7 @@ static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd,
if (!asd)
return false;
- if (pipe_id == IA_CSS_PIPE_ID_ACC || pipe_id == IA_CSS_PIPE_ID_YUVPP)
+ if (pipe_id == IA_CSS_PIPE_ID_YUVPP)
return true;
if (asd->vfpp) {
@@ -718,12 +702,7 @@ static int __create_pipe(struct atomisp_sub_device *asd,
if (pipe_id >= IA_CSS_PIPE_ID_NUM)
return -EINVAL;
- if (pipe_id != IA_CSS_PIPE_ID_ACC &&
- !stream_env->pipe_configs[pipe_id].output_info[0].res.width)
- return 0;
-
- if (pipe_id == IA_CSS_PIPE_ID_ACC &&
- !stream_env->pipe_configs[pipe_id].acc_extension)
+ if (!stream_env->pipe_configs[pipe_id].output_info[0].res.width)
return 0;
if (!is_pipe_valid_to_current_run_mode(asd, pipe_id))
@@ -885,48 +864,10 @@ int atomisp_css_load_firmware(struct atomisp_device *isp)
void atomisp_css_uninit(struct atomisp_device *isp)
{
- struct atomisp_sub_device *asd;
- unsigned int i;
-
- for (i = 0; i < isp->num_of_streams; i++) {
- asd = &isp->asd[i];
- memset(&asd->params.config, 0, sizeof(asd->params.config));
- asd->params.css_update_params_needed = false;
- }
-
- isp->css_initialized = false;
- ia_css_uninit();
-}
-
-void atomisp_css_suspend(struct atomisp_device *isp)
-{
isp->css_initialized = false;
ia_css_uninit();
}
-int atomisp_css_resume(struct atomisp_device *isp)
-{
- unsigned int mmu_base_addr;
- int ret;
-
- ret = hmm_get_mmu_base_addr(isp->dev, &mmu_base_addr);
- if (ret) {
- dev_err(isp->dev, "get base address error.\n");
- return -EINVAL;
- }
-
- ret = ia_css_init(isp->dev, &isp->css_env.isp_css_env, NULL,
- mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE);
- if (ret) {
- dev_err(isp->dev, "re-init css failed.\n");
- return -EINVAL;
- }
- ia_css_enable_isys_event_queue(true);
-
- isp->css_initialized = true;
- return 0;
-}
-
int atomisp_css_irq_translate(struct atomisp_device *isp,
unsigned int *infos)
{
@@ -996,7 +937,7 @@ void atomisp_css_init_struct(struct atomisp_sub_device *asd)
}
int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd,
- struct videobuf_vmalloc_memory *vm_mem,
+ struct ia_css_frame *frame,
enum atomisp_input_stream_id stream_id,
enum ia_css_buffer_type css_buf_type,
enum ia_css_pipe_id css_pipe_id)
@@ -1006,7 +947,7 @@ int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd,
int err;
css_buf.type = css_buf_type;
- css_buf.data.frame = vm_mem->vaddr;
+ css_buf.data.frame = frame;
err = ia_css_pipe_enqueue_buffer(
stream_env->pipes[css_pipe_id], &css_buf);
@@ -2141,8 +2082,6 @@ static enum ia_css_pipe_mode __pipe_id_to_pipe_mode(
return IA_CSS_PIPE_MODE_CAPTURE;
case IA_CSS_PIPE_ID_VIDEO:
return IA_CSS_PIPE_MODE_VIDEO;
- case IA_CSS_PIPE_ID_ACC:
- return IA_CSS_PIPE_MODE_ACC;
case IA_CSS_PIPE_ID_YUVPP:
return IA_CSS_PIPE_MODE_YUVPP;
default:
@@ -2688,7 +2627,7 @@ int atomisp_get_css_frame_info(struct atomisp_sub_device *asd,
if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index]
.pipes[pipe_index], &info)) {
- dev_err(isp->dev, "ia_css_pipe_get_info FAILED");
+ dev_dbg(isp->dev, "ia_css_pipe_get_info FAILED");
return -EINVAL;
}
@@ -2765,29 +2704,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd,
return 0;
}
-int atomisp_css_yuvpp_configure_viewfinder(
- struct atomisp_sub_device *asd,
- unsigned int stream_index,
- unsigned int width, unsigned int height,
- unsigned int min_width,
- enum ia_css_frame_format format)
-{
- struct atomisp_stream_env *stream_env =
- &asd->stream_env[stream_index];
- enum ia_css_pipe_id pipe_id = IA_CSS_PIPE_ID_YUVPP;
-
- stream_env->pipe_configs[pipe_id].mode =
- __pipe_id_to_pipe_mode(asd, pipe_id);
- stream_env->update_pipe[pipe_id] = true;
-
- stream_env->pipe_configs[pipe_id].vf_output_info[0].res.width = width;
- stream_env->pipe_configs[pipe_id].vf_output_info[0].res.height = height;
- stream_env->pipe_configs[pipe_id].vf_output_info[0].format = format;
- stream_env->pipe_configs[pipe_id].vf_output_info[0].padded_width =
- min_width;
- return 0;
-}
-
int atomisp_css_yuvpp_get_output_frame_info(
struct atomisp_sub_device *asd,
unsigned int stream_index,
@@ -3180,7 +3096,7 @@ static int atomisp_compare_dvs_grid(struct atomisp_sub_device *asd,
}
if (sizeof(*cur) != sizeof(*atomgrid)) {
- dev_err(asd->isp->dev, "dvs grid mis-match!\n");
+ dev_err(asd->isp->dev, "dvs grid mismatch!\n");
return -EINVAL;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c
index 84a84e0cdeef..acea7492847d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_fops.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c
@@ -22,7 +22,7 @@
#include <linux/pm_runtime.h>
#include <media/v4l2-ioctl.h>
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
#include "atomisp_cmd.h"
#include "atomisp_common.h"
@@ -35,49 +35,74 @@
#include "atomisp-regs.h"
#include "hmm/hmm.h"
+#include "ia_css_frame.h"
#include "type_support.h"
#include "device_access/device_access.h"
-#define ISP_LEFT_PAD 128 /* equal to 2*NWAY */
-
/*
- * input image data, and current frame resolution for test
+ * Videobuf2 ops
*/
-#define ISP_PARAM_MMAP_OFFSET 0xfffff000
+static int atomisp_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct atomisp_video_pipe *pipe = container_of(vq, struct atomisp_video_pipe, vb_queue);
+ u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev);
+ int ret;
-#define MAGIC_CHECK(is, should) \
- do { \
- if (unlikely((is) != (should))) { \
- pr_err("magic mismatch: %x (expected %x)\n", \
- is, should); \
- BUG(); \
- } \
- } while (0)
+ mutex_lock(&pipe->asd->isp->mutex); /* for get_css_frame_info() / set_fmt() */
-/*
- * Videobuf ops
- */
-static int atomisp_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct atomisp_video_pipe *pipe = vq->priv_data;
+ /*
+ * When VIDIOC_S_FMT has not been called before VIDIOC_REQBUFS, then
+ * this will fail. Call atomisp_set_fmt() ourselves and try again.
+ */
+ ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info);
+ if (ret) {
+ struct v4l2_format f = {
+ .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420,
+ .fmt.pix.width = 10000,
+ .fmt.pix.height = 10000,
+ };
+
+ ret = atomisp_set_fmt(&pipe->vdev, &f);
+ if (ret)
+ goto out;
+
+ ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info);
+ if (ret)
+ goto out;
+ }
+
+ atomisp_alloc_css_stat_bufs(pipe->asd, ATOMISP_INPUT_STREAM_GENERAL);
- *size = pipe->pix.sizeimage;
+ *nplanes = 1;
+ sizes[0] = PAGE_ALIGN(pipe->pix.sizeimage);
+out:
+ mutex_unlock(&pipe->asd->isp->mutex);
return 0;
}
-static int atomisp_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int atomisp_buf_init(struct vb2_buffer *vb)
{
- struct atomisp_video_pipe *pipe = vq->priv_data;
+ struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
+ struct ia_css_frame *frame = vb_to_frame(vb);
+ int ret;
+
+ ret = ia_css_frame_init_from_info(frame, &pipe->frame_info);
+ if (ret)
+ return ret;
- vb->size = pipe->pix.sizeimage;
- vb->width = pipe->pix.width;
- vb->height = pipe->pix.height;
- vb->field = field;
- vb->state = VIDEOBUF_PREPARED;
+ if (frame->data_bytes > vb2_plane_size(vb, 0)) {
+ dev_err(pipe->asd->isp->dev, "Internal error frame.data_bytes(%u) > vb.length(%lu)\n",
+ frame->data_bytes, vb2_plane_size(vb, 0));
+ return -EIO;
+ }
+
+ frame->data = hmm_create_from_vmalloc_buf(vb2_plane_size(vb, 0),
+ vb2_plane_vaddr(vb, 0));
+ if (frame->data == mmgr_NULL)
+ return -ENOMEM;
return 0;
}
@@ -156,8 +181,7 @@ static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd,
} else {
list_add_tail(&s3a_buf->list, &asd->s3a_stats_in_css);
if (s3a_list == &asd->s3a_stats_ready)
- dev_warn(asd->isp->dev, "%s: drop one s3a stat which has exp_id %d!\n",
- __func__, exp_id);
+ dev_dbg(asd->isp->dev, "drop one s3a stat with exp_id %d\n", exp_id);
}
asd->s3a_bufs_in_css[css_pipe_id]++;
@@ -206,43 +230,44 @@ static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd,
return 0;
}
-int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
- struct atomisp_video_pipe *pipe,
- enum atomisp_input_stream_id stream_id,
- enum ia_css_buffer_type css_buf_type,
- enum ia_css_pipe_id css_pipe_id)
+static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
+ struct atomisp_video_pipe *pipe,
+ enum atomisp_input_stream_id stream_id,
+ enum ia_css_buffer_type css_buf_type,
+ enum ia_css_pipe_id css_pipe_id)
{
- struct videobuf_vmalloc_memory *vm_mem;
struct atomisp_css_params_with_list *param;
struct ia_css_dvs_grid_info *dvs_grid =
atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);
unsigned long irqflags;
- int err = 0;
+ int space, err = 0;
+
+ lockdep_assert_held(&asd->isp->mutex);
if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM))
return -EINVAL;
- while (pipe->buffers_in_css < ATOMISP_CSS_Q_DEPTH) {
- struct videobuf_buffer *vb;
+ if (pipe->stopping)
+ return -EINVAL;
+
+ space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe);
+ while (space--) {
+ struct ia_css_frame *frame;
spin_lock_irqsave(&pipe->irq_lock, irqflags);
- if (list_empty(&pipe->activeq)) {
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
- return -EINVAL;
- }
- vb = list_entry(pipe->activeq.next,
- struct videobuf_buffer, queue);
- list_del_init(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
+ frame = list_first_entry_or_null(&pipe->activeq, struct ia_css_frame, queue);
+ if (frame)
+ list_move_tail(&frame->queue, &pipe->buffers_in_css);
spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+ if (!frame)
+ return -EINVAL;
+
/*
* If there is a per_frame setting to apply on the buffer,
* do it before buffer en-queueing.
*/
- vm_mem = vb->priv;
-
- param = pipe->frame_params[vb->i];
+ param = pipe->frame_params[frame->vb.vb2_buf.index];
if (param) {
atomisp_makeup_css_parameters(asd,
&asd->params.css_param.update_flag,
@@ -256,8 +281,7 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
if (!err)
asd->params.config.dz_config = &param->params.dz_config;
}
- atomisp_css_set_isp_config_applied_frame(asd,
- vm_mem->vaddr);
+ atomisp_css_set_isp_config_applied_frame(asd, frame);
atomisp_css_update_isp_params_on_pipe(asd,
asd->stream_env[stream_id].pipes[css_pipe_id]);
asd->params.dvs_6axis = (struct ia_css_dvs_6axis_config *)
@@ -282,20 +306,19 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
&asd->params.css_param.dz_config;
asd->params.css_update_params_needed = true;
}
+ pipe->frame_params[frame->vb.vb2_buf.index] = NULL;
}
/* Enqueue buffer */
- err = atomisp_q_video_buffer_to_css(asd, vm_mem, stream_id,
+ err = atomisp_q_video_buffer_to_css(asd, frame, stream_id,
css_buf_type, css_pipe_id);
if (err) {
spin_lock_irqsave(&pipe->irq_lock, irqflags);
- list_add_tail(&vb->queue, &pipe->activeq);
- vb->state = VIDEOBUF_QUEUED;
+ list_move_tail(&frame->queue, &pipe->activeq);
spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
dev_err(asd->isp->dev, "%s, css q fails: %d\n",
__func__, err);
return -EINVAL;
}
- pipe->buffers_in_css++;
/* enqueue 3A/DIS/metadata buffers */
if (asd->params.curr_grid_info.s3a_grid.enable &&
@@ -517,11 +540,32 @@ int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd)
return 0;
}
-static void atomisp_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void atomisp_buf_queue(struct vb2_buffer *vb)
{
- struct atomisp_video_pipe *pipe = vq->priv_data;
+ struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
+ struct ia_css_frame *frame = vb_to_frame(vb);
+ struct atomisp_sub_device *asd = pipe->asd;
+ u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev);
+ unsigned long irqflags;
+ int ret;
+
+ mutex_lock(&asd->isp->mutex);
+
+ ret = atomisp_pipe_check(pipe, false);
+ if (ret || pipe->stopping) {
+ spin_lock_irqsave(&pipe->irq_lock, irqflags);
+ atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+ goto out_unlock;
+ }
+
+ /* FIXME this ugliness comes from the original atomisp buffer handling */
+ if (!(vb->skip_cache_sync_on_finish && vb->skip_cache_sync_on_prepare))
+ wbinvd();
+ pipe->frame_params[vb->index] = NULL;
+
+ spin_lock_irqsave(&pipe->irq_lock, irqflags);
/*
* when a frame buffer meets following conditions, it should be put into
* the waiting list:
@@ -533,40 +577,83 @@ static void atomisp_buf_queue(struct videobuf_queue *vq,
* get enqueued.
*/
if (!atomisp_is_vf_pipe(pipe) &&
- (pipe->frame_request_config_id[vb->i] ||
+ (pipe->frame_request_config_id[vb->index] ||
!list_empty(&pipe->buffers_waiting_for_param)))
- list_add_tail(&vb->queue, &pipe->buffers_waiting_for_param);
+ list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param);
else
- list_add_tail(&vb->queue, &pipe->activeq);
+ list_add_tail(&frame->queue, &pipe->activeq);
+
+ spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
+
+ /* TODO: do this better, not best way to queue to css */
+ if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) {
+ if (!list_empty(&pipe->buffers_waiting_for_param))
+ atomisp_handle_parameter_and_buffer(pipe);
+ else
+ atomisp_qbuffers_to_css(asd);
+ }
+
+ /*
+ * Workaround: Due to the design of HALv3,
+ * sometimes in ZSL or SDV mode HAL needs to
+ * capture multiple images within one streaming cycle.
+ * But the capture number cannot be determined by HAL.
+ * So HAL only sets the capture number to be 1 and queue multiple
+ * buffers. Atomisp driver needs to check this case and re-trigger
+ * CSS to do capture when new buffer is queued.
+ */
+ if (asd->continuous_mode->val && source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE &&
+ !asd->enable_raw_buffer_lock->val && asd->params.offline_parm.num_captures == 1) {
+ asd->pending_capture_request++;
+ dev_dbg(asd->isp->dev, "Add one pending capture request.\n");
+ }
- vb->state = VIDEOBUF_QUEUED;
+out_unlock:
+ mutex_unlock(&asd->isp->mutex);
}
-static void atomisp_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void atomisp_buf_cleanup(struct vb2_buffer *vb)
{
- vb->state = VIDEOBUF_NEEDS_INIT;
- atomisp_videobuf_free_buf(vb);
+ struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
+ struct ia_css_frame *frame = vb_to_frame(vb);
+ int index = frame->vb.vb2_buf.index;
+
+ pipe->frame_request_config_id[index] = 0;
+ pipe->frame_params[index] = NULL;
+
+ hmm_free(frame->data);
}
-static const struct videobuf_queue_ops videobuf_qops = {
- .buf_setup = atomisp_buf_setup,
- .buf_prepare = atomisp_buf_prepare,
- .buf_queue = atomisp_buf_queue,
- .buf_release = atomisp_buf_release,
+static const struct vb2_ops atomisp_vb2_ops = {
+ .queue_setup = atomisp_queue_setup,
+ .buf_init = atomisp_buf_init,
+ .buf_cleanup = atomisp_buf_cleanup,
+ .buf_queue = atomisp_buf_queue,
+ .start_streaming = atomisp_start_streaming,
+ .stop_streaming = atomisp_stop_streaming,
};
static int atomisp_init_pipe(struct atomisp_video_pipe *pipe)
{
+ int ret;
+
/* init locks */
spin_lock_init(&pipe->irq_lock);
+ mutex_init(&pipe->vb_queue_mutex);
+
+ /* Init videobuf2 queue structure */
+ pipe->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pipe->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ pipe->vb_queue.buf_struct_size = sizeof(struct ia_css_frame);
+ pipe->vb_queue.ops = &atomisp_vb2_ops;
+ pipe->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ pipe->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ ret = vb2_queue_init(&pipe->vb_queue);
+ if (ret)
+ return ret;
- videobuf_queue_vmalloc_init(&pipe->capq, &videobuf_qops, NULL,
- &pipe->irq_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
- sizeof(struct atomisp_buffer), pipe,
- NULL); /* ext_lock: NULL */
+ pipe->vdev.queue = &pipe->vb_queue;
+ pipe->vdev.queue->lock = &pipe->vb_queue_mutex;
INIT_LIST_HEAD(&pipe->activeq);
INIT_LIST_HEAD(&pipe->buffers_waiting_for_param);
@@ -721,13 +808,6 @@ static int atomisp_open(struct file *file)
goto error;
}
- /* Init ISP */
- if (atomisp_css_init(isp)) {
- ret = -EINVAL;
- /* Need to clean up CSS init if it fails. */
- goto css_error;
- }
-
atomisp_dev_init_struct(isp);
ret = v4l2_subdev_call(isp->flash, core, s_power, 1);
@@ -752,7 +832,6 @@ done:
return 0;
css_error:
- atomisp_css_uninit(isp);
pm_runtime_put(vdev->v4l2_dev->dev);
error:
mutex_unlock(&isp->mutex);
@@ -766,45 +845,26 @@ static int atomisp_release(struct file *file)
struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
struct atomisp_sub_device *asd = pipe->asd;
- struct v4l2_requestbuffers req;
struct v4l2_subdev_fh fh;
struct v4l2_rect clear_compose = {0};
unsigned long flags;
- int ret = 0;
+ int ret;
v4l2_fh_init(&fh.vfh, vdev);
- req.count = 0;
- if (!isp)
- return -EBADF;
-
- mutex_lock(&isp->mutex);
-
dev_dbg(isp->dev, "release device %s\n", vdev->name);
asd->subdev.devnode = vdev;
- pipe->users--;
-
- if (pipe->capq.streaming)
- dev_warn(isp->dev,
- "%s: ISP still streaming while closing!",
- __func__);
+ /* Note file must not be used after this! */
+ vb2_fop_release(file);
- if (pipe->capq.streaming &&
- atomisp_streamoff(file, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
- dev_err(isp->dev, "atomisp_streamoff failed on release, driver bug");
- goto done;
- }
+ mutex_lock(&isp->mutex);
+ pipe->users--;
if (pipe->users)
goto done;
- if (atomisp_reqbufs(file, NULL, &req)) {
- dev_err(isp->dev, "atomisp_reqbufs failed on release, driver bug");
- goto done;
- }
-
/*
* A little trick here:
* file injection input resolution is recorded in the sink pad,
@@ -840,7 +900,6 @@ static int atomisp_release(struct file *file)
goto done;
atomisp_destroy_pipes_stream_force(asd);
- atomisp_css_uninit(isp);
if (defer_fw_load) {
ia_css_unload_firmware();
@@ -862,260 +921,15 @@ done:
V4L2_SEL_TGT_COMPOSE, 0,
&clear_compose);
mutex_unlock(&isp->mutex);
-
- return v4l2_fh_release(file);
-}
-
-/*
- * Memory help functions for image frame and private parameters
- */
-static int do_isp_mm_remap(struct atomisp_device *isp,
- struct vm_area_struct *vma,
- ia_css_ptr isp_virt, u32 host_virt, u32 pgnr)
-{
- u32 pfn;
-
- while (pgnr) {
- pfn = hmm_virt_to_phys(isp_virt) >> PAGE_SHIFT;
- if (remap_pfn_range(vma, host_virt, pfn,
- PAGE_SIZE, PAGE_SHARED)) {
- dev_err(isp->dev, "remap_pfn_range err.\n");
- return -EAGAIN;
- }
-
- isp_virt += PAGE_SIZE;
- host_virt += PAGE_SIZE;
- pgnr--;
- }
-
- return 0;
-}
-
-static int frame_mmap(struct atomisp_device *isp,
- const struct ia_css_frame *frame, struct vm_area_struct *vma)
-{
- ia_css_ptr isp_virt;
- u32 host_virt;
- u32 pgnr;
-
- if (!frame) {
- dev_err(isp->dev, "%s: NULL frame pointer.\n", __func__);
- return -EINVAL;
- }
-
- host_virt = vma->vm_start;
- isp_virt = frame->data;
- pgnr = DIV_ROUND_UP(frame->data_bytes, PAGE_SIZE);
-
- if (do_isp_mm_remap(isp, vma, isp_virt, host_virt, pgnr))
- return -EAGAIN;
-
return 0;
}
-int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q,
- struct vm_area_struct *vma)
-{
- u32 offset = vma->vm_pgoff << PAGE_SHIFT;
- int ret = -EINVAL, i;
- struct atomisp_device *isp =
- ((struct atomisp_video_pipe *)(q->priv_data))->isp;
- struct videobuf_vmalloc_memory *vm_mem;
- struct videobuf_mapping *map;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
- if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
- dev_err(isp->dev, "map appl bug: PROT_WRITE and MAP_SHARED are required\n");
- return -EINVAL;
- }
-
- mutex_lock(&q->vb_lock);
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- struct videobuf_buffer *buf = q->bufs[i];
-
- if (!buf)
- continue;
-
- map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
- if (!map) {
- mutex_unlock(&q->vb_lock);
- return -ENOMEM;
- }
-
- buf->map = map;
- map->q = q;
-
- buf->baddr = vma->vm_start;
-
- if (buf && buf->memory == V4L2_MEMORY_MMAP &&
- buf->boff == offset) {
- vm_mem = buf->priv;
- ret = frame_mmap(isp, vm_mem->vaddr, vma);
- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
- break;
- }
- }
- mutex_unlock(&q->vb_lock);
-
- return ret;
-}
-
-/* The input frame contains left and right padding that need to be removed.
- * There is always ISP_LEFT_PAD padding on the left side.
- * There is also padding on the right (padded_width - width).
- */
-static int remove_pad_from_frame(struct atomisp_device *isp,
- struct ia_css_frame *in_frame, __u32 width, __u32 height)
-{
- unsigned int i;
- unsigned short *buffer;
- int ret = 0;
- ia_css_ptr load = in_frame->data;
- ia_css_ptr store = load;
-
- buffer = kmalloc_array(width, sizeof(load), GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- load += ISP_LEFT_PAD;
- for (i = 0; i < height; i++) {
- ret = hmm_load(load, buffer, width * sizeof(load));
- if (ret < 0)
- goto remove_pad_error;
-
- ret = hmm_store(store, buffer, width * sizeof(store));
- if (ret < 0)
- goto remove_pad_error;
-
- load += in_frame->info.padded_width;
- store += width;
- }
-
-remove_pad_error:
- kfree(buffer);
- return ret;
-}
-
-static int atomisp_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_device *isp = video_get_drvdata(vdev);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- struct atomisp_sub_device *asd = pipe->asd;
- struct ia_css_frame *raw_virt_addr;
- u32 start = vma->vm_start;
- u32 end = vma->vm_end;
- u32 size = end - start;
- u32 origin_size, new_size;
- int ret;
-
- if (!asd) {
- dev_err(isp->dev, "%s(): asd is NULL, device is %s\n",
- __func__, vdev->name);
- return -EINVAL;
- }
-
- if (!(vma->vm_flags & (VM_WRITE | VM_READ)))
- return -EACCES;
-
- mutex_lock(&isp->mutex);
-
- if (!(vma->vm_flags & VM_SHARED)) {
- /* Map private buffer.
- * Set VM_SHARED to the flags since we need
- * to map the buffer page by page.
- * Without VM_SHARED, remap_pfn_range() treats
- * this kind of mapping as invalid.
- */
- vma->vm_flags |= VM_SHARED;
- ret = hmm_mmap(vma, vma->vm_pgoff << PAGE_SHIFT);
- mutex_unlock(&isp->mutex);
- return ret;
- }
-
- /* mmap for ISP offline raw data */
- if (atomisp_subdev_source_pad(vdev)
- == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE &&
- vma->vm_pgoff == (ISP_PARAM_MMAP_OFFSET >> PAGE_SHIFT)) {
- new_size = pipe->pix.width * pipe->pix.height * 2;
- if (asd->params.online_process != 0) {
- ret = -EINVAL;
- goto error;
- }
- raw_virt_addr = asd->raw_output_frame;
- if (!raw_virt_addr) {
- dev_err(isp->dev, "Failed to request RAW frame\n");
- ret = -EINVAL;
- goto error;
- }
-
- ret = remove_pad_from_frame(isp, raw_virt_addr,
- pipe->pix.width, pipe->pix.height);
- if (ret < 0) {
- dev_err(isp->dev, "remove pad failed.\n");
- goto error;
- }
- origin_size = raw_virt_addr->data_bytes;
- raw_virt_addr->data_bytes = new_size;
-
- if (size != PAGE_ALIGN(new_size)) {
- dev_err(isp->dev, "incorrect size for mmap ISP Raw Frame\n");
- ret = -EINVAL;
- goto error;
- }
-
- if (frame_mmap(isp, raw_virt_addr, vma)) {
- dev_err(isp->dev, "frame_mmap failed.\n");
- raw_virt_addr->data_bytes = origin_size;
- ret = -EAGAIN;
- goto error;
- }
- raw_virt_addr->data_bytes = origin_size;
- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
- mutex_unlock(&isp->mutex);
- return 0;
- }
-
- /*
- * mmap for normal frames
- */
- if (size != pipe->pix.sizeimage) {
- dev_err(isp->dev, "incorrect size for mmap ISP frames\n");
- ret = -EINVAL;
- goto error;
- }
- mutex_unlock(&isp->mutex);
-
- return atomisp_videobuf_mmap_mapper(&pipe->capq, vma);
-
-error:
- mutex_unlock(&isp->mutex);
-
- return ret;
-}
-
-static __poll_t atomisp_poll(struct file *file,
- struct poll_table_struct *pt)
-{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_device *isp = video_get_drvdata(vdev);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
-
- mutex_lock(&isp->mutex);
- if (pipe->capq.streaming != 1) {
- mutex_unlock(&isp->mutex);
- return EPOLLERR;
- }
- mutex_unlock(&isp->mutex);
-
- return videobuf_poll_stream(file, &pipe->capq, pt);
-}
-
const struct v4l2_file_operations atomisp_fops = {
.owner = THIS_MODULE,
.open = atomisp_open,
.release = atomisp_release,
- .mmap = atomisp_mmap,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
/*
@@ -1124,5 +938,4 @@ const struct v4l2_file_operations atomisp_fops = {
.compat_ioctl32 = atomisp_compat_ioctl32,
*/
#endif
- .poll = atomisp_poll,
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.h b/drivers/staging/media/atomisp/pci/atomisp_fops.h
index 3f1e442ba782..10e43126b693 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_fops.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_fops.h
@@ -22,12 +22,6 @@
#define __ATOMISP_FOPS_H__
#include "atomisp_subdev.h"
-int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
- struct atomisp_video_pipe *pipe,
- enum atomisp_input_stream_id stream_id,
- enum ia_css_buffer_type css_buf_type,
- enum ia_css_pipe_id css_pipe_id);
-
unsigned int atomisp_dev_users(struct atomisp_device *isp);
unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd);
@@ -35,13 +29,6 @@ unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd);
* Memory help functions for image frame and private parameters
*/
-int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q,
- struct vm_area_struct *vma);
-
-int atomisp_qbuf_to_css(struct atomisp_device *isp,
- struct atomisp_video_pipe *pipe,
- struct videobuf_buffer *vb);
-
int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd);
extern const struct v4l2_file_operations atomisp_fops;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h
index d9d158cdf09e..653e6d74a966 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
@@ -195,7 +195,6 @@ struct atomisp_regs {
};
struct atomisp_sw_contex {
- int power_state;
int running_freq;
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index 0ddb0ed42dd9..cb01ba65c88f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -23,7 +23,6 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/videobuf-vmalloc.h>
#include "atomisp_cmd.h"
#include "atomisp_common.h"
@@ -542,6 +541,11 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool settings_change)
if (pipe->isp->isp_fatal_error)
return -EIO;
+ if (settings_change && vb2_is_busy(&pipe->vb_queue)) {
+ dev_err(pipe->isp->dev, "Set fmt/input IOCTL while streaming\n");
+ return -EBUSY;
+ }
+
switch (pipe->asd->streaming) {
case ATOMISP_DEVICE_STREAMING_DISABLED:
break;
@@ -632,10 +636,10 @@ static int atomisp_enum_input(struct file *file, void *fh,
static unsigned int
atomisp_subdev_streaming_count(struct atomisp_sub_device *asd)
{
- return asd->video_out_preview.capq.streaming
- + asd->video_out_capture.capq.streaming
- + asd->video_out_video_capture.capq.streaming
- + asd->video_out_vf.capq.streaming;
+ return asd->video_out_preview.vb_queue.start_streaming_called
+ + asd->video_out_capture.vb_queue.start_streaming_called
+ + asd->video_out_video_capture.vb_queue.start_streaming_called
+ + asd->video_out_vf.vb_queue.start_streaming_called;
}
unsigned int atomisp_streaming_count(struct atomisp_device *isp)
@@ -661,6 +665,14 @@ static int atomisp_g_input(struct file *file, void *fh, unsigned int *input)
return 0;
}
+static int atomisp_s_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return atomisp_set_fmt(vdev, f);
+}
+
/*
* set input are used to set current primary/secondary camera
*/
@@ -866,29 +878,8 @@ static int atomisp_adjust_fmt(struct v4l2_format *f)
u32 padded_width;
format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat);
-
- padded_width = f->fmt.pix.width + pad_w;
-
- if (format_bridge->planar) {
- f->fmt.pix.bytesperline = padded_width;
- f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height *
- DIV_ROUND_UP(format_bridge->depth *
- padded_width, 8));
- } else {
- f->fmt.pix.bytesperline = DIV_ROUND_UP(format_bridge->depth *
- padded_width, 8);
- f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * f->fmt.pix.bytesperline);
- }
-
- if (f->fmt.pix.field == V4L2_FIELD_ANY)
- f->fmt.pix.field = V4L2_FIELD_NONE;
-
- format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat);
- if (!format_bridge)
- return -EINVAL;
-
/* Currently, raw formats are broken!!! */
- if (format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ if (!format_bridge || format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat);
@@ -931,6 +922,7 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh,
struct v4l2_format *f)
{
struct video_device *vdev = video_devdata(file);
+ u32 pixfmt = f->fmt.pix.pixelformat;
int ret;
/*
@@ -944,6 +936,12 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh,
if (ret)
return ret;
+ /*
+ * atomisp_try_fmt() replaces pixelformat with the sensors native
+ * format, restore the actual format requested by userspace.
+ */
+ f->fmt.pix.pixelformat = pixfmt;
+
return atomisp_adjust_fmt(f);
}
@@ -961,44 +959,13 @@ static int atomisp_g_fmt_cap(struct file *file, void *fh,
if (f->fmt.pix.sizeimage)
return 0;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
f->fmt.pix.width = 10000;
f->fmt.pix.height = 10000;
return atomisp_try_fmt_cap(file, fh, f);
}
-/*
- * Free videobuffer buffer priv data
- */
-void atomisp_videobuf_free_buf(struct videobuf_buffer *vb)
-{
- struct videobuf_vmalloc_memory *vm_mem;
-
- if (!vb)
- return;
-
- vm_mem = vb->priv;
- if (vm_mem && vm_mem->vaddr) {
- ia_css_frame_free(vm_mem->vaddr);
- vm_mem->vaddr = NULL;
- }
-}
-
-/*
- * this function is used to free video buffer queue
- */
-static void atomisp_videobuf_free_queue(struct videobuf_queue *q)
-{
- int i;
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- atomisp_videobuf_free_buf(q->bufs[i]);
- kfree(q->bufs[i]);
- q->bufs[i] = NULL;
- }
-}
-
int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd,
uint16_t stream_id)
{
@@ -1100,178 +1067,13 @@ error:
return -ENOMEM;
}
-/*
- * Initiate Memory Mapping or User Pointer I/O
- */
-int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req)
-{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- struct atomisp_sub_device *asd = pipe->asd;
- struct ia_css_frame_info frame_info;
- struct ia_css_frame *frame;
- struct videobuf_vmalloc_memory *vm_mem;
- u16 source_pad = atomisp_subdev_source_pad(vdev);
- int ret = 0, i = 0;
-
- if (req->count == 0) {
- mutex_lock(&pipe->capq.vb_lock);
- if (!list_empty(&pipe->capq.stream))
- videobuf_queue_cancel(&pipe->capq);
-
- atomisp_videobuf_free_queue(&pipe->capq);
- mutex_unlock(&pipe->capq.vb_lock);
- /* clear request config id */
- memset(pipe->frame_request_config_id, 0,
- VIDEO_MAX_FRAME * sizeof(unsigned int));
- memset(pipe->frame_params, 0,
- VIDEO_MAX_FRAME *
- sizeof(struct atomisp_css_params_with_list *));
- return 0;
- }
-
- ret = videobuf_reqbufs(&pipe->capq, req);
- if (ret)
- return ret;
-
- atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL);
-
- /*
- * for user pointer type, buffers are not really allocated here,
- * buffers are setup in QBUF operation through v4l2_buffer structure
- */
- if (req->memory == V4L2_MEMORY_USERPTR)
- return 0;
-
- ret = atomisp_get_css_frame_info(asd, source_pad, &frame_info);
- if (ret)
- return ret;
-
- /*
- * Allocate the real frame here for selected node using our
- * memory management function
- */
- for (i = 0; i < req->count; i++) {
- if (ia_css_frame_allocate_from_info(&frame, &frame_info))
- goto error;
- vm_mem = pipe->capq.bufs[i]->priv;
- vm_mem->vaddr = frame;
- }
-
- return ret;
-
-error:
- while (i--) {
- vm_mem = pipe->capq.bufs[i]->priv;
- ia_css_frame_free(vm_mem->vaddr);
- }
-
- if (asd->vf_frame)
- ia_css_frame_free(asd->vf_frame);
-
- return -ENOMEM;
-}
-
-/* application query the status of a buffer */
-static int atomisp_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
-
- return videobuf_querybuf(&pipe->capq, buf);
-}
-
-/*
- * Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or
- * filled (output) buffer in the drivers incoming queue.
- */
-static int atomisp_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+static int atomisp_qbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- static const int NOFLUSH_FLAGS = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE |
- V4L2_BUF_FLAG_NO_CACHE_CLEAN;
struct video_device *vdev = video_devdata(file);
struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- struct atomisp_sub_device *asd = pipe->asd;
- struct videobuf_buffer *vb;
- struct videobuf_vmalloc_memory *vm_mem;
- struct ia_css_frame_info frame_info;
- struct ia_css_frame *handle = NULL;
- u32 length;
- u32 pgnr;
- int ret;
-
- ret = atomisp_pipe_check(pipe, false);
- if (ret)
- return ret;
-
- if (!buf || buf->index >= VIDEO_MAX_FRAME ||
- !pipe->capq.bufs[buf->index]) {
- dev_err(isp->dev, "Invalid index for qbuf.\n");
- return -EINVAL;
- }
-
- /*
- * For userptr type frame, we convert user space address to physic
- * address and reprograme out page table properly
- */
- if (buf->memory == V4L2_MEMORY_USERPTR) {
- if (offset_in_page(buf->m.userptr)) {
- dev_err(isp->dev, "Error userptr is not page aligned.\n");
- return -EINVAL;
- }
-
- vb = pipe->capq.bufs[buf->index];
- vm_mem = vb->priv;
- if (!vm_mem)
- return -EINVAL;
-
- length = vb->bsize;
- pgnr = (length + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
-
- if (vb->baddr == buf->m.userptr && vm_mem->vaddr)
- goto done;
-
- if (atomisp_get_css_frame_info(asd,
- atomisp_subdev_source_pad(vdev), &frame_info))
- return -EIO;
-
- ret = ia_css_frame_map(&handle, &frame_info,
- (void __user *)buf->m.userptr,
- pgnr);
- if (ret) {
- dev_err(isp->dev, "Failed to map user buffer\n");
- return ret;
- }
-
- if (vm_mem->vaddr) {
- mutex_lock(&pipe->capq.vb_lock);
- ia_css_frame_free(vm_mem->vaddr);
- vm_mem->vaddr = NULL;
- vb->state = VIDEOBUF_NEEDS_INIT;
- mutex_unlock(&pipe->capq.vb_lock);
- }
-
- vm_mem->vaddr = handle;
-
- buf->flags &= ~V4L2_BUF_FLAG_MAPPED;
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- buf->flags &= ~V4L2_BUF_FLAG_DONE;
- } else if (buf->memory == V4L2_MEMORY_MMAP) {
- buf->flags |= V4L2_BUF_FLAG_MAPPED;
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- buf->flags &= ~V4L2_BUF_FLAG_DONE;
-
- /*
- * For mmap, frames were allocated at request buffers
- */
- }
-
-done:
- if (!((buf->flags & NOFLUSH_FLAGS) == NOFLUSH_FLAGS))
- wbinvd();
+ /* FIXME this abuse of buf->reserved2 comes from the original atomisp buffer handling */
if (!atomisp_is_vf_pipe(pipe) &&
(buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING)) {
/* this buffer will have a per-frame parameter */
@@ -1284,90 +1086,27 @@ done:
pipe->frame_request_config_id[buf->index] = 0;
}
- pipe->frame_params[buf->index] = NULL;
-
- mutex_unlock(&isp->mutex);
- ret = videobuf_qbuf(&pipe->capq, buf);
- mutex_lock(&isp->mutex);
- if (ret)
- return ret;
-
- /* TODO: do this better, not best way to queue to css */
- if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) {
- if (!list_empty(&pipe->buffers_waiting_for_param)) {
- atomisp_handle_parameter_and_buffer(pipe);
- } else {
- atomisp_qbuffers_to_css(asd);
- }
- }
-
- /*
- * Workaround: Due to the design of HALv3,
- * sometimes in ZSL or SDV mode HAL needs to
- * capture multiple images within one streaming cycle.
- * But the capture number cannot be determined by HAL.
- * So HAL only sets the capture number to be 1 and queue multiple
- * buffers. Atomisp driver needs to check this case and re-trigger
- * CSS to do capture when new buffer is queued.
- */
- if (asd->continuous_mode->val &&
- atomisp_subdev_source_pad(vdev)
- == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE &&
- pipe->capq.streaming &&
- !asd->enable_raw_buffer_lock->val &&
- asd->params.offline_parm.num_captures == 1) {
- asd->pending_capture_request++;
- dev_dbg(isp->dev, "Add one pending capture request.\n");
- }
-
- dev_dbg(isp->dev, "qbuf buffer %d (%s) for asd%d\n", buf->index,
- vdev->name, asd->index);
-
- return 0;
+ return vb2_ioctl_qbuf(file, fh, buf);
}
-static int __get_frame_exp_id(struct atomisp_video_pipe *pipe,
- struct v4l2_buffer *buf)
-{
- struct videobuf_vmalloc_memory *vm_mem;
- struct ia_css_frame *handle;
- int i;
-
- for (i = 0; pipe->capq.bufs[i]; i++) {
- vm_mem = pipe->capq.bufs[i]->priv;
- handle = vm_mem->vaddr;
- if (buf->index == pipe->capq.bufs[i]->i && handle)
- return handle->exp_id;
- }
- return -EINVAL;
-}
-
-/*
- * Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or
- * displayed (output buffer)from the driver's outgoing queue
- */
-static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct video_device *vdev = video_devdata(file);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
struct atomisp_sub_device *asd = pipe->asd;
struct atomisp_device *isp = video_get_drvdata(vdev);
+ struct ia_css_frame *frame;
+ struct vb2_buffer *vb;
int ret;
- ret = atomisp_pipe_check(pipe, false);
+ ret = vb2_ioctl_dqbuf(file, fh, buf);
if (ret)
return ret;
- mutex_unlock(&isp->mutex);
- ret = videobuf_dqbuf(&pipe->capq, buf, file->f_flags & O_NONBLOCK);
- mutex_lock(&isp->mutex);
- if (ret) {
- if (ret != -EAGAIN)
- dev_dbg(isp->dev, "<%s: %d\n", __func__, ret);
- return ret;
- }
+ vb = pipe->vb_queue.bufs[buf->index];
+ frame = vb_to_frame(vb);
- buf->bytesused = pipe->pix.sizeimage;
+ /* FIXME this abuse of buf->reserved* comes from the original atomisp buffer handling */
buf->reserved = asd->frame_status[buf->index];
/*
@@ -1378,7 +1117,7 @@ static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
*/
buf->reserved &= 0x0000ffff;
if (!(buf->flags & V4L2_BUF_FLAG_ERROR))
- buf->reserved |= __get_frame_exp_id(pipe, buf) << 16;
+ buf->reserved |= frame->exp_id;
buf->reserved2 = pipe->frame_config_id[buf->index];
dev_dbg(isp->dev,
@@ -1506,36 +1245,26 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd)
atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00);
}
-/*
- * This ioctl start the capture during streaming I/O.
- */
-static int atomisp_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
+int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
+ struct atomisp_video_pipe *pipe = vq_to_pipe(vq);
struct atomisp_sub_device *asd = pipe->asd;
- struct atomisp_device *isp = video_get_drvdata(vdev);
+ struct video_device *vdev = &pipe->vdev;
+ struct atomisp_device *isp = asd->isp;
struct pci_dev *pdev = to_pci_dev(isp->dev);
enum ia_css_pipe_id css_pipe_id;
unsigned int sensor_start_stream;
unsigned long irqflags;
int ret;
+ mutex_lock(&isp->mutex);
+
dev_dbg(isp->dev, "Start stream on pad %d for asd%d\n",
atomisp_subdev_source_pad(vdev), asd->index);
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_dbg(isp->dev, "unsupported v4l2 buf type\n");
- return -EINVAL;
- }
-
ret = atomisp_pipe_check(pipe, false);
if (ret)
- return ret;
-
- if (pipe->capq.streaming)
- return 0;
+ goto out_unlock;
/* Input system HW workaround */
atomisp_dma_burst_len_cfg(asd);
@@ -1546,18 +1275,6 @@ static int atomisp_streamon(struct file *file, void *fh,
*/
sensor_start_stream = atomisp_sensor_start_stream(asd);
- spin_lock_irqsave(&pipe->irq_lock, irqflags);
- if (list_empty(&pipe->capq.stream)) {
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
- dev_dbg(isp->dev, "no buffer in the queue\n");
- return -EINVAL;
- }
- spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
-
- ret = videobuf_streamon(&pipe->capq);
- if (ret)
- return ret;
-
/* Reset pending capture request count. */
asd->pending_capture_request = 0;
@@ -1578,8 +1295,10 @@ static int atomisp_streamon(struct file *file, void *fh,
mutex_unlock(&isp->mutex);
ret = wait_for_completion_interruptible(&asd->init_done);
mutex_lock(&isp->mutex);
- if (ret != 0)
- return -ERESTARTSYS;
+ if (ret) {
+ ret = -ERESTARTSYS;
+ goto out_unlock;
+ }
}
/* handle per_frame_setting parameter and buffers */
@@ -1601,12 +1320,15 @@ static int atomisp_streamon(struct file *file, void *fh,
asd->params.offline_parm.num_captures,
asd->params.offline_parm.skip_frames,
asd->params.offline_parm.offset);
- if (ret)
- return -EINVAL;
+ if (ret) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
}
}
atomisp_qbuffers_to_css(asd);
- return 0;
+ ret = 0;
+ goto out_unlock;
}
if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) {
@@ -1631,8 +1353,10 @@ static int atomisp_streamon(struct file *file, void *fh,
asd->params.dvs_6axis = NULL;
ret = atomisp_css_start(asd, css_pipe_id, false);
- if (ret)
- return ret;
+ if (ret) {
+ atomisp_flush_video_pipe(pipe, true);
+ goto out_unlock;
+ }
spin_lock_irqsave(&isp->lock, irqflags);
asd->streaming = ATOMISP_DEVICE_STREAMING_ENABLED;
@@ -1652,8 +1376,10 @@ static int atomisp_streamon(struct file *file, void *fh,
atomisp_qbuffers_to_css(asd);
/* Only start sensor when the last streaming instance started */
- if (atomisp_subdev_streaming_count(asd) < sensor_start_stream)
- return 0;
+ if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) {
+ ret = 0;
+ goto out_unlock;
+ }
start_sensor:
if (isp->flash) {
@@ -1684,7 +1410,7 @@ start_sensor:
ret = atomisp_stream_on_master_slave_sensor(isp, false);
if (ret) {
dev_err(isp->dev, "master slave sensor stream on failed!\n");
- return ret;
+ goto out_unlock;
}
goto start_delay_wq;
} else if (asd->depth_mode->val && (atomisp_streaming_count(isp) <
@@ -1706,7 +1432,8 @@ start_sensor:
spin_lock_irqsave(&isp->lock, irqflags);
asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED;
spin_unlock_irqrestore(&isp->lock, irqflags);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
start_delay_wq:
@@ -1722,35 +1449,44 @@ start_delay_wq:
asd->delayed_init = ATOMISP_DELAYED_INIT_NOT_QUEUED;
}
- return 0;
+out_unlock:
+ mutex_unlock(&isp->mutex);
+ return ret;
}
-int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+void atomisp_stop_streaming(struct vb2_queue *vq)
{
- struct video_device *vdev = video_devdata(file);
- struct atomisp_device *isp = video_get_drvdata(vdev);
- struct pci_dev *pdev = to_pci_dev(isp->dev);
- struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
+ struct atomisp_video_pipe *pipe = vq_to_pipe(vq);
struct atomisp_sub_device *asd = pipe->asd;
- struct atomisp_video_pipe *capture_pipe = NULL;
- struct atomisp_video_pipe *vf_pipe = NULL;
- struct atomisp_video_pipe *preview_pipe = NULL;
- struct atomisp_video_pipe *video_pipe = NULL;
- struct videobuf_buffer *vb, *_vb;
+ struct video_device *vdev = &pipe->vdev;
+ struct atomisp_device *isp = asd->isp;
+ struct pci_dev *pdev = to_pci_dev(isp->dev);
+ bool recreate_streams[MAX_STREAM_NUM] = {0};
enum ia_css_pipe_id css_pipe_id;
- int ret;
- unsigned long flags;
bool first_streamoff = false;
+ unsigned long flags;
+ int i, ret;
+
+ mutex_lock(&isp->mutex);
dev_dbg(isp->dev, "Stop stream on pad %d for asd%d\n",
atomisp_subdev_source_pad(vdev), asd->index);
- lockdep_assert_held(&isp->mutex);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_dbg(isp->dev, "unsupported v4l2 buf type\n");
- return -EINVAL;
- }
+ /*
+ * There is no guarantee that the buffers queued to / owned by the ISP
+ * will properly be returned to the queue when stopping. Set a flag to
+ * avoid new buffers getting queued and then wait for all the current
+ * buffers to finish.
+ */
+ pipe->stopping = true;
+ mutex_unlock(&isp->mutex);
+ /* wait max 1 second */
+ ret = wait_event_timeout(pipe->vb_queue.done_wq,
+ atomisp_buffers_in_css(pipe) == 0, HZ);
+ mutex_lock(&isp->mutex);
+ pipe->stopping = false;
+ if (ret == 0)
+ dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n");
/*
* do only videobuf_streamoff for capture & vf pipes in
@@ -1766,36 +1502,10 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
0, 0, 0);
atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, false);
}
- /*
- * Currently there is no way to flush buffers queued to css.
- * When doing videobuf_streamoff, active buffers will be
- * marked as VIDEOBUF_NEEDS_INIT. HAL will be able to use
- * these buffers again, and these buffers might be queued to
- * css more than once! Warn here, if HAL has not dequeued all
- * buffers back before calling streamoff.
- */
- if (pipe->buffers_in_css != 0) {
- WARN(1, "%s: buffers of vdev %s still in CSS!\n",
- __func__, pipe->vdev.name);
-
- /*
- * Buffers remained in css maybe dequeued out in the
- * next stream on, while this will causes serious
- * issues as buffers already get invalid after
- * previous stream off.
- *
- * No way to flush buffers but to reset the whole css
- */
- dev_warn(isp->dev, "Reset CSS to clean up css buffers.\n");
- atomisp_css_flush(isp);
- }
- return videobuf_streamoff(&pipe->capq);
+ goto out_unlock;
}
- if (!pipe->capq.streaming)
- return 0;
-
if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED)
first_streamoff = true;
@@ -1806,12 +1516,8 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING;
spin_unlock_irqrestore(&isp->lock, flags);
- if (!first_streamoff) {
- ret = videobuf_streamoff(&pipe->capq);
- if (ret)
- return ret;
+ if (!first_streamoff)
goto stopsensor;
- }
atomisp_clear_css_buffer_counters(asd);
atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false);
@@ -1824,48 +1530,12 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
css_pipe_id = atomisp_get_css_pipe_id(asd);
atomisp_css_stop(asd, css_pipe_id, false);
- /* cancel work queue*/
- if (asd->video_out_capture.users) {
- capture_pipe = &asd->video_out_capture;
- wake_up_interruptible(&capture_pipe->capq.wait);
- }
- if (asd->video_out_vf.users) {
- vf_pipe = &asd->video_out_vf;
- wake_up_interruptible(&vf_pipe->capq.wait);
- }
- if (asd->video_out_preview.users) {
- preview_pipe = &asd->video_out_preview;
- wake_up_interruptible(&preview_pipe->capq.wait);
- }
- if (asd->video_out_video_capture.users) {
- video_pipe = &asd->video_out_video_capture;
- wake_up_interruptible(&video_pipe->capq.wait);
- }
- ret = videobuf_streamoff(&pipe->capq);
- if (ret)
- return ret;
-
- /* cleanup css here */
- /* no need for this, as ISP will be reset anyway */
- /*atomisp_flush_bufs_in_css(isp);*/
-
- spin_lock_irqsave(&pipe->irq_lock, flags);
- list_for_each_entry_safe(vb, _vb, &pipe->activeq, queue) {
- vb->state = VIDEOBUF_PREPARED;
- list_del(&vb->queue);
- }
- list_for_each_entry_safe(vb, _vb, &pipe->buffers_waiting_for_param, queue) {
- vb->state = VIDEOBUF_PREPARED;
- list_del(&vb->queue);
- pipe->frame_request_config_id[vb->i] = 0;
- }
- spin_unlock_irqrestore(&pipe->irq_lock, flags);
+ atomisp_flush_video_pipe(pipe, true);
atomisp_subdev_cleanup_pending_events(asd);
stopsensor:
- if (atomisp_subdev_streaming_count(asd) + 1
- != atomisp_sensor_start_stream(asd))
- return 0;
+ if (atomisp_subdev_streaming_count(asd) != atomisp_sensor_start_stream(asd))
+ goto out_unlock;
ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
video, s_stream, 0);
@@ -1878,7 +1548,7 @@ stopsensor:
/* if other streams are running, isp should not be powered off */
if (atomisp_streaming_count(isp)) {
atomisp_css_flush(isp);
- return 0;
+ goto out_unlock;
}
/* Disable the CSI interface on ANN B0/K0 */
@@ -1894,50 +1564,45 @@ stopsensor:
* ISP work around, need to reset isp
* Is it correct time to reset ISP when first node does streamoff?
*/
- if (isp->sw_contex.power_state == ATOM_ISP_POWER_UP) {
- unsigned int i;
- bool recreate_streams[MAX_STREAM_NUM] = {0};
-
- if (isp->isp_timeout)
- dev_err(isp->dev, "%s: Resetting with WA activated",
- __func__);
- /*
- * It is possible that the other asd stream is in the stage
- * that v4l2_setfmt is just get called on it, which will
- * create css stream on that stream. But at this point, there
- * is no way to destroy the css stream created on that stream.
- *
- * So force stream destroy here.
- */
- for (i = 0; i < isp->num_of_streams; i++) {
- if (isp->asd[i].stream_prepared) {
- atomisp_destroy_pipes_stream_force(&isp->
- asd[i]);
- recreate_streams[i] = true;
- }
+ if (isp->isp_timeout)
+ dev_err(isp->dev, "%s: Resetting with WA activated",
+ __func__);
+ /*
+ * It is possible that the other asd stream is in the stage
+ * that v4l2_setfmt is just get called on it, which will
+ * create css stream on that stream. But at this point, there
+ * is no way to destroy the css stream created on that stream.
+ *
+ * So force stream destroy here.
+ */
+ for (i = 0; i < isp->num_of_streams; i++) {
+ if (isp->asd[i].stream_prepared) {
+ atomisp_destroy_pipes_stream_force(&isp->asd[i]);
+ recreate_streams[i] = true;
}
+ }
- /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */
- pci_write_config_dword(pdev, PCI_I_CONTROL,
- isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK);
- dev_err(isp->dev, "atomisp_reset");
- atomisp_reset(isp);
- for (i = 0; i < isp->num_of_streams; i++) {
- if (recreate_streams[i]) {
- int ret2;
-
- ret2 = atomisp_create_pipes_stream(&isp->asd[i]);
- if (ret2) {
- dev_err(isp->dev, "%s error re-creating streams: %d\n",
- __func__, ret2);
- if (!ret)
- ret = ret2;
- }
+ /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */
+ pci_write_config_dword(pdev, PCI_I_CONTROL,
+ isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK);
+ dev_err(isp->dev, "atomisp_reset");
+ atomisp_reset(isp);
+ for (i = 0; i < isp->num_of_streams; i++) {
+ if (recreate_streams[i]) {
+ int ret2;
+
+ ret2 = atomisp_create_pipes_stream(&isp->asd[i]);
+ if (ret2) {
+ dev_err(isp->dev, "%s error re-creating streams: %d\n",
+ __func__, ret2);
+ if (!ret)
+ ret = ret2;
}
}
- isp->isp_timeout = false;
}
- return ret;
+ isp->isp_timeout = false;
+out_unlock:
+ mutex_unlock(&isp->mutex);
}
/*
@@ -2725,13 +2390,13 @@ const struct v4l2_ioctl_ops atomisp_ioctl_ops = {
.vidioc_enum_fmt_vid_cap = atomisp_enum_fmt_cap,
.vidioc_try_fmt_vid_cap = atomisp_try_fmt_cap,
.vidioc_g_fmt_vid_cap = atomisp_g_fmt_cap,
- .vidioc_s_fmt_vid_cap = atomisp_set_fmt,
- .vidioc_reqbufs = atomisp_reqbufs,
- .vidioc_querybuf = atomisp_querybuf,
- .vidioc_qbuf = atomisp_qbuf,
- .vidioc_dqbuf = atomisp_dqbuf,
- .vidioc_streamon = atomisp_streamon,
- .vidioc_streamoff = atomisp_streamoff,
+ .vidioc_s_fmt_vid_cap = atomisp_s_fmt_cap,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = atomisp_qbuf_wrapper,
+ .vidioc_dqbuf = atomisp_dqbuf_wrapper,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_default = atomisp_vidioc_default,
.vidioc_s_parm = atomisp_s_parm,
.vidioc_g_parm = atomisp_g_parm,
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h
index c660f631d371..59e071f035f9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h
@@ -39,14 +39,12 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool streaming_ok);
int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd,
uint16_t stream_id);
-int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type);
-int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req);
+int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count);
+void atomisp_stop_streaming(struct vb2_queue *vq);
enum ia_css_pipe_id atomisp_get_css_pipe_id(struct atomisp_sub_device
*asd);
-void atomisp_videobuf_free_buf(struct videobuf_buffer *vb);
-
extern const struct v4l2_ioctl_ops atomisp_ioctl_ops;
unsigned int atomisp_streaming_count(struct atomisp_device *isp);
@@ -57,4 +55,8 @@ long atomisp_compat_ioctl32(struct file *file,
int atomisp_stream_on_master_slave_sensor(struct atomisp_device *isp,
bool isp_timeout);
+
+int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count);
+void atomisp_stop_streaming(struct vb2_queue *vq);
+
#endif /* __ATOMISP_IOCTL_H__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index 847dfee6ad78..cadc468b4c2f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -1064,6 +1064,8 @@ static void atomisp_init_subdev_pipe(struct atomisp_sub_device *asd,
pipe->asd = asd;
pipe->isp = asd->isp;
spin_lock_init(&pipe->irq_lock);
+ mutex_init(&pipe->vb_queue_mutex);
+ INIT_LIST_HEAD(&pipe->buffers_in_css);
INIT_LIST_HEAD(&pipe->activeq);
INIT_LIST_HEAD(&pipe->buffers_waiting_for_param);
INIT_LIST_HEAD(&pipe->per_frame_params);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
index a1f4da35235d..bd2872cbb50c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
@@ -21,8 +21,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#include <media/videobuf-core.h>
-
+#include <media/videobuf2-v4l2.h>
#include "atomisp_common.h"
#include "atomisp_compat.h"
#include "atomisp_v4l2.h"
@@ -69,7 +68,12 @@ struct atomisp_video_pipe {
struct video_device vdev;
enum v4l2_buf_type type;
struct media_pad pad;
- struct videobuf_queue capq;
+ struct vb2_queue vb_queue;
+ /* Lock for vb_queue, when also taking isp->mutex this must be taken first! */
+ struct mutex vb_queue_mutex;
+ /* List of video-buffers handed over to the CSS */
+ struct list_head buffers_in_css;
+ /* List of video-buffers handed over to the driver, but not yet to the CSS */
struct list_head activeq;
/*
* the buffers waiting for per-frame parameters, this is only valid
@@ -79,10 +83,13 @@ struct atomisp_video_pipe {
/* the link list to store per_frame parameters */
struct list_head per_frame_params;
+ /* Filled through atomisp_get_css_frame_info() on queue setup */
+ struct ia_css_frame_info frame_info;
+
/* Store here the initial run mode */
unsigned int default_run_mode;
-
- unsigned int buffers_in_css;
+ /* Set from streamoff to disallow queuing further buffers in CSS */
+ bool stopping;
/*
* irq_lock is used to protect video buffer state change operations and
@@ -110,6 +117,11 @@ struct atomisp_video_pipe {
struct atomisp_css_params_with_list *frame_params[VIDEO_MAX_FRAME];
};
+#define vq_to_pipe(queue) \
+ container_of(queue, struct atomisp_video_pipe, vb_queue)
+
+#define vb_to_pipe(vb) vq_to_pipe((vb)->vb2_queue)
+
struct atomisp_pad_format {
struct v4l2_mbus_framefmt fmt;
struct v4l2_rect crop;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index d5bb9906ca6f..e786b81921da 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -573,11 +573,7 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp)
unsigned long flags;
spin_lock_irqsave(&isp->lock, flags);
- if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) {
- spin_unlock_irqrestore(&isp->lock, flags);
- dev_dbg(isp->dev, "<%s %d.\n", __func__, __LINE__);
- return 0;
- }
+
/*
* MRFLD HAS requirement: cannot power off i-unit if
* ISP has IRQ not serviced.
@@ -724,62 +720,51 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable)
return -EBUSY;
}
-/* Workaround for pmu_nc_set_power_state not ready in MRFLD */
-int atomisp_mrfld_power_down(struct atomisp_device *isp)
-{
- return atomisp_mrfld_power(isp, false);
-}
-
-/* Workaround for pmu_nc_set_power_state not ready in MRFLD */
-int atomisp_mrfld_power_up(struct atomisp_device *isp)
-{
- return atomisp_mrfld_power(isp, true);
-}
-
-int atomisp_runtime_suspend(struct device *dev)
+int atomisp_power_off(struct device *dev)
{
- struct atomisp_device *isp = (struct atomisp_device *)
- dev_get_drvdata(dev);
+ struct atomisp_device *isp = dev_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
int ret;
+ u32 reg;
+
+ atomisp_css_uninit(isp);
ret = atomisp_mrfld_pre_power_down(isp);
if (ret)
return ret;
- /*Turn off the ISP d-phy*/
- ret = atomisp_ospm_dphy_down(isp);
- if (ret)
- return ret;
+ /*
+ * MRFLD IUNIT DPHY is located in an always-power-on island
+ * MRFLD HW design need all CSI ports are disabled before
+ * powering down the IUNIT.
+ */
+ pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, &reg);
+ reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK;
+ pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg);
+
cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE);
- return atomisp_mrfld_power_down(isp);
+ return atomisp_mrfld_power(isp, false);
}
-int atomisp_runtime_resume(struct device *dev)
+int atomisp_power_on(struct device *dev)
{
struct atomisp_device *isp = (struct atomisp_device *)
dev_get_drvdata(dev);
int ret;
- ret = atomisp_mrfld_power_up(isp);
+ ret = atomisp_mrfld_power(isp, true);
if (ret)
return ret;
cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency);
- if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) {
- /*Turn on ISP d-phy */
- ret = atomisp_ospm_dphy_up(isp);
- if (ret) {
- dev_err(isp->dev, "Failed to power up ISP!.\n");
- return -EINVAL;
- }
- }
/*restore register values for iUnit and iUnitPHY registers*/
if (isp->saved_regs.pcicmdsts)
atomisp_restore_iunit_reg(isp);
atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true);
- return 0;
+
+ return atomisp_css_init(isp);
}
static int __maybe_unused atomisp_suspend(struct device *dev)
@@ -789,7 +774,6 @@ static int __maybe_unused atomisp_suspend(struct device *dev)
/* FIXME: only has one isp_subdev at present */
struct atomisp_sub_device *asd = &isp->asd[0];
unsigned long flags;
- int ret;
/*
* FIXME: Suspend is not supported by sensors. Abort if any video
@@ -806,45 +790,12 @@ static int __maybe_unused atomisp_suspend(struct device *dev)
}
spin_unlock_irqrestore(&isp->lock, flags);
- ret = atomisp_mrfld_pre_power_down(isp);
- if (ret)
- return ret;
-
- /*Turn off the ISP d-phy */
- ret = atomisp_ospm_dphy_down(isp);
- if (ret) {
- dev_err(isp->dev, "fail to power off ISP\n");
- return ret;
- }
- cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE);
- return atomisp_mrfld_power_down(isp);
+ return atomisp_power_off(dev);
}
static int __maybe_unused atomisp_resume(struct device *dev)
{
- struct atomisp_device *isp = (struct atomisp_device *)
- dev_get_drvdata(dev);
- int ret;
-
- ret = atomisp_mrfld_power_up(isp);
- if (ret)
- return ret;
-
- cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency);
-
- /*Turn on ISP d-phy */
- ret = atomisp_ospm_dphy_up(isp);
- if (ret) {
- dev_err(isp->dev, "Failed to power up ISP!.\n");
- return -EINVAL;
- }
-
- /*restore register values for iUnit and iUnitPHY registers*/
- if (isp->saved_regs.pcicmdsts)
- atomisp_restore_iunit_reg(isp);
-
- atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true);
- return 0;
+ return atomisp_power_on(dev);
}
int atomisp_csi_lane_config(struct atomisp_device *isp)
@@ -1459,7 +1410,6 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
isp->dev = &pdev->dev;
isp->base = pcim_iomap_table(pdev)[ATOM_ISP_PCI_BAR];
- isp->sw_contex.power_state = ATOM_ISP_POWER_UP;
isp->saved_regs.ispmmadr = start;
dev_dbg(&pdev->dev, "atomisp mmio base: %p\n", isp->base);
@@ -1723,10 +1673,8 @@ load_fw_fail:
atomisp_msi_irq_uninit(isp);
- atomisp_ospm_dphy_down(isp);
-
/* Address later when we worry about the ...field chips */
- if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power_down(isp))
+ if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power(isp, false))
dev_err(&pdev->dev, "Failed to switch off ISP\n");
atomisp_dev_alloc_fail:
@@ -1774,8 +1722,8 @@ static const struct pci_device_id atomisp_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, atomisp_pci_tbl);
static const struct dev_pm_ops atomisp_pm_ops = {
- .runtime_suspend = atomisp_runtime_suspend,
- .runtime_resume = atomisp_runtime_resume,
+ .runtime_suspend = atomisp_power_off,
+ .runtime_resume = atomisp_power_on,
.suspend = atomisp_suspend,
.resume = atomisp_resume,
};
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h
index 965cfda50707..e42eeaeb3ee4 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h
@@ -16,6 +16,8 @@
#ifndef __IA_CSS_PIPE_BINARYDESC_H__
#define __IA_CSS_PIPE_BINARYDESC_H__
+#include <linux/math.h>
+
#include <ia_css_types.h> /* ia_css_pipe */
#include <ia_css_frame_public.h> /* ia_css_frame_info */
#include <ia_css_binary.h> /* ia_css_binary_descr */
@@ -56,17 +58,12 @@ void ia_css_pipe_get_vfpp_binarydesc(
*
* @param[in] bds_factor: The bayer downscaling factor.
* (= The bds_factor member in the sh_css_bds_factor structure.)
- * @param[out] bds_factor_numerator: The numerator of the bayer downscaling factor.
- * (= The numerator member in the sh_css_bds_factor structure.)
- * @param[out] bds_factor_denominator: The denominator of the bayer downscaling factor.
- * (= The denominator member in the sh_css_bds_factor structure.)
+ * @param[out] bds: The rational fraction of the bayer downscaling factor.
+ * (= The respective member in the sh_css_bds_factor structure.)
* @return 0 or error code upon error.
*
*/
-int sh_css_bds_factor_get_numerator_denominator(
- unsigned int bds_factor,
- unsigned int *bds_factor_numerator,
- unsigned int *bds_factor_denominator);
+int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds);
/* @brief Get a binary descriptor for preview stage.
*
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h
index 40c8145a0797..7a0c988d89ee 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h
@@ -38,11 +38,6 @@ void ia_css_pipe_get_firmwares_stage_desc(
const struct ia_css_fw_info *fw,
unsigned int mode);
-void ia_css_pipe_get_acc_stage_desc(
- struct ia_css_pipeline_stage_desc *stage_desc,
- struct ia_css_binary *binary,
- struct ia_css_fw_info *fw);
-
void ia_css_pipe_get_sp_func_stage_desc(
struct ia_css_pipeline_stage_desc *stage_desc,
struct ia_css_frame *out_frame,
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c
index 7dd0e4a53c8b..06664ce75b60 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c
@@ -13,6 +13,9 @@
* more details.
*/
+#include <linux/kernel.h>
+#include <linux/math.h>
+
#include "ia_css_pipe_binarydesc.h"
#include "ia_css_frame_format.h"
#include "ia_css_pipe.h"
@@ -23,7 +26,6 @@
#include <assert_support.h>
/* HRT_GDC_N */
#include "gdc_device.h"
-#include <linux/kernel.h>
/* This module provides a binary descriptions to used to find a binary. Since,
* every stage is associated with a binary, it implicity helps stage
@@ -126,40 +128,29 @@ void ia_css_pipe_get_vfpp_binarydesc(
IA_CSS_LEAVE_PRIVATE("");
}
-static struct sh_css_bds_factor bds_factors_list[] = {
- {1, 1, SH_CSS_BDS_FACTOR_1_00},
- {5, 4, SH_CSS_BDS_FACTOR_1_25},
- {3, 2, SH_CSS_BDS_FACTOR_1_50},
- {2, 1, SH_CSS_BDS_FACTOR_2_00},
- {9, 4, SH_CSS_BDS_FACTOR_2_25},
- {5, 2, SH_CSS_BDS_FACTOR_2_50},
- {3, 1, SH_CSS_BDS_FACTOR_3_00},
- {4, 1, SH_CSS_BDS_FACTOR_4_00},
- {9, 2, SH_CSS_BDS_FACTOR_4_50},
- {5, 1, SH_CSS_BDS_FACTOR_5_00},
- {6, 1, SH_CSS_BDS_FACTOR_6_00},
- {8, 1, SH_CSS_BDS_FACTOR_8_00}
+static struct u32_fract bds_factors_list[] = {
+ [SH_CSS_BDS_FACTOR_1_00] = {1, 1},
+ [SH_CSS_BDS_FACTOR_1_25] = {5, 4},
+ [SH_CSS_BDS_FACTOR_1_50] = {3, 2},
+ [SH_CSS_BDS_FACTOR_2_00] = {2, 1},
+ [SH_CSS_BDS_FACTOR_2_25] = {9, 4},
+ [SH_CSS_BDS_FACTOR_2_50] = {5, 2},
+ [SH_CSS_BDS_FACTOR_3_00] = {3, 1},
+ [SH_CSS_BDS_FACTOR_4_00] = {4, 1},
+ [SH_CSS_BDS_FACTOR_4_50] = {9, 2},
+ [SH_CSS_BDS_FACTOR_5_00] = {5, 1},
+ [SH_CSS_BDS_FACTOR_6_00] = {6, 1},
+ [SH_CSS_BDS_FACTOR_8_00] = {8, 1},
};
-int sh_css_bds_factor_get_numerator_denominator(
- unsigned int bds_factor,
- unsigned int *bds_factor_numerator,
- unsigned int *bds_factor_denominator)
+int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds)
{
- unsigned int i;
-
- /* Loop over all bds factors until a match is found */
- for (i = 0; i < ARRAY_SIZE(bds_factors_list); i++) {
- if (bds_factors_list[i].bds_factor == bds_factor) {
- *bds_factor_numerator = bds_factors_list[i].numerator;
- *bds_factor_denominator = bds_factors_list[i].denominator;
- return 0;
- }
- }
+ /* Throw an error since bds_factor cannot be found in bds_factors_list */
+ if (bds_factor >= ARRAY_SIZE(bds_factors_list))
+ return -EINVAL;
- /* Throw an error since bds_factor cannot be found
- in bds_factors_list */
- return -EINVAL;
+ *bds = bds_factors_list[bds_factor];
+ return 0;
}
int binarydesc_calculate_bds_factor(
@@ -194,7 +185,7 @@ int binarydesc_calculate_bds_factor(
(out_h * num / den <= in_h);
if (cond) {
- *bds_factor = bds_factors_list[i].bds_factor;
+ *bds_factor = i;
return 0;
}
}
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
index 82a24aabe8ce..6c93fa1c683b 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
@@ -74,27 +74,6 @@ void ia_css_pipe_get_firmwares_stage_desc(
stage_desc->vf_frame = vf_frame;
}
-void ia_css_pipe_get_acc_stage_desc(
- struct ia_css_pipeline_stage_desc *stage_desc,
- struct ia_css_binary *binary,
- struct ia_css_fw_info *fw)
-{
- unsigned int i;
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
- "ia_css_pipe_get_acc_stage_desc() enter:\n");
- stage_desc->binary = binary;
- stage_desc->firmware = fw;
- stage_desc->sp_func = IA_CSS_PIPELINE_NO_FUNC;
- stage_desc->max_input_width = 0;
- stage_desc->mode = IA_CSS_BINARY_MODE_VF_PP;
- stage_desc->in_frame = NULL;
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
- stage_desc->out_frame[i] = NULL;
- }
- stage_desc->vf_frame = NULL;
-}
-
void ia_css_pipe_get_sp_func_stage_desc(
struct ia_css_pipeline_stage_desc *stage_desc,
struct ia_css_frame *out_frame,
diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h
index 1c7938d8ccb5..8f79424bedb2 100644
--- a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h
+++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h
@@ -161,7 +161,7 @@ STORAGE_CLASS_PIXELGEN_C void pixelgen_ctrl_dump_state(
state->syng_stat_fcnt);
ia_css_print("Pixel Generator ID %d syng stat done 0x%x\n", ID,
state->syng_stat_done);
- ia_css_print("Pixel Generator ID %d tpg modee 0x%x\n", ID, state->tpg_mode);
+ ia_css_print("Pixel Generator ID %d tpg mode 0x%x\n", ID, state->tpg_mode);
ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID,
state->tpg_hcnt_mask);
ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID,
diff --git a/drivers/staging/media/atomisp/pci/hive_types.h b/drivers/staging/media/atomisp/pci/hive_types.h
index 4b8a679fb672..55d36931f079 100644
--- a/drivers/staging/media/atomisp/pci/hive_types.h
+++ b/drivers/staging/media/atomisp/pci/hive_types.h
@@ -42,7 +42,7 @@ typedef unsigned int hive_bool;
#define hive_false 0
#define hive_true 1
-typedef char hive_int8;
+typedef signed char hive_int8;
typedef short hive_int16;
typedef int hive_int32;
typedef long long hive_int64;
diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c
index fc6cfe9f7744..bb12644fd033 100644
--- a/drivers/staging/media/atomisp/pci/hmm/hmm.c
+++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c
@@ -41,11 +41,9 @@ static bool hmm_initialized;
/*
* p: private
- * s: shared
- * u: user
- * i: ion
+ * v: vmalloc
*/
-static const char hmm_bo_type_string[] = "psui";
+static const char hmm_bo_type_string[] = "pv";
static ssize_t bo_show(struct device *dev, struct device_attribute *attr,
char *buf, struct list_head *bo_list, bool active)
@@ -168,7 +166,8 @@ void hmm_cleanup(void)
hmm_initialized = false;
}
-static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __user *userptr)
+static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type,
+ void *vmalloc_addr)
{
unsigned int pgnr;
struct hmm_buffer_object *bo;
@@ -192,7 +191,7 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __
}
/* Allocate pages for memory */
- ret = hmm_bo_alloc_pages(bo, type, userptr);
+ ret = hmm_bo_alloc_pages(bo, type, vmalloc_addr);
if (ret) {
dev_err(atomisp_dev, "hmm_bo_alloc_pages failed.\n");
goto alloc_page_err;
@@ -205,9 +204,8 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __
goto bind_err;
}
- dev_dbg(atomisp_dev,
- "%s: pages: 0x%08x (%zu bytes), type: %d, user ptr %p\n",
- __func__, bo->start, bytes, type, userptr);
+ dev_dbg(atomisp_dev, "pages: 0x%08x (%zu bytes), type: %d, vmalloc %p\n",
+ bo->start, bytes, type, vmalloc);
return bo->start;
@@ -224,9 +222,9 @@ ia_css_ptr hmm_alloc(size_t bytes)
return __hmm_alloc(bytes, HMM_BO_PRIVATE, NULL);
}
-ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr)
+ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr)
{
- return __hmm_alloc(bytes, HMM_BO_USER, userptr);
+ return __hmm_alloc(bytes, HMM_BO_VMALLOC, vmalloc_addr);
}
void hmm_free(ia_css_ptr virt)
diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
index a5fd6d38d3c4..5e53eed8ae95 100644
--- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
+++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
@@ -638,6 +638,7 @@ static int alloc_private_pages(struct hmm_buffer_object *bo)
ret = alloc_pages_bulk_array(gfp, bo->pgnr, bo->pages);
if (ret != bo->pgnr) {
free_pages_bulk_array(ret, bo->pages);
+ dev_err(atomisp_dev, "alloc_pages_bulk_array() failed\n");
return -ENOMEM;
}
@@ -651,61 +652,34 @@ static int alloc_private_pages(struct hmm_buffer_object *bo)
return 0;
}
-static void free_user_pages(struct hmm_buffer_object *bo,
- unsigned int page_nr)
+static int alloc_vmalloc_pages(struct hmm_buffer_object *bo, void *vmalloc_addr)
{
+ void *vaddr = vmalloc_addr;
int i;
- for (i = 0; i < page_nr; i++)
- put_page(bo->pages[i]);
-}
-
-/*
- * Convert user space virtual address into pages list
- */
-static int alloc_user_pages(struct hmm_buffer_object *bo,
- const void __user *userptr)
-{
- int page_nr;
-
- userptr = untagged_addr(userptr);
-
- /* Handle frame buffer allocated in user space */
- mutex_unlock(&bo->mutex);
- page_nr = get_user_pages_fast((unsigned long)userptr, bo->pgnr, 1, bo->pages);
- mutex_lock(&bo->mutex);
-
- /* can be written by caller, not forced */
- if (page_nr != bo->pgnr) {
- dev_err(atomisp_dev,
- "get_user_pages err: bo->pgnr = %d, pgnr actually pinned = %d.\n",
- bo->pgnr, page_nr);
- if (page_nr < 0)
- page_nr = 0;
- goto out_of_mem;
+ for (i = 0; i < bo->pgnr; i++) {
+ bo->pages[i] = vmalloc_to_page(vaddr);
+ if (!bo->pages[i]) {
+ dev_err(atomisp_dev, "Error could not get page %d of vmalloc buf\n", i);
+ return -ENOMEM;
+ }
+ vaddr += PAGE_SIZE;
}
return 0;
-
-out_of_mem:
-
- free_user_pages(bo, page_nr);
-
- return -ENOMEM;
}
/*
* allocate/free physical pages for the bo.
*
* type indicate where are the pages from. currently we have 3 types
- * of memory: HMM_BO_PRIVATE, HMM_BO_USER.
+ * of memory: HMM_BO_PRIVATE, HMM_BO_VMALLOC.
*
- * userptr is only valid when type is HMM_BO_USER, it indicates
- * the start address from user space task.
+ * vmalloc_addr is only valid when type is HMM_BO_VMALLOC.
*/
int hmm_bo_alloc_pages(struct hmm_buffer_object *bo,
enum hmm_bo_type type,
- const void __user *userptr)
+ void *vmalloc_addr)
{
int ret = -EINVAL;
@@ -720,14 +694,10 @@ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo,
goto alloc_err;
}
- /*
- * TO DO:
- * add HMM_BO_USER type
- */
if (type == HMM_BO_PRIVATE) {
ret = alloc_private_pages(bo);
- } else if (type == HMM_BO_USER) {
- ret = alloc_user_pages(bo, userptr);
+ } else if (type == HMM_BO_VMALLOC) {
+ ret = alloc_vmalloc_pages(bo, vmalloc_addr);
} else {
dev_err(atomisp_dev, "invalid buffer type.\n");
ret = -EINVAL;
@@ -771,8 +741,8 @@ void hmm_bo_free_pages(struct hmm_buffer_object *bo)
if (bo->type == HMM_BO_PRIVATE)
free_private_bo_pages(bo);
- else if (bo->type == HMM_BO_USER)
- free_user_pages(bo, bo->pgnr);
+ else if (bo->type == HMM_BO_VMALLOC)
+ ; /* No-op, nothing to do */
else
dev_err(atomisp_dev, "invalid buffer type.\n");
diff --git a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
index 514d933f934d..7ba464abf447 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h
@@ -20,6 +20,7 @@
* This file contains structs to describe various frame-formats supported by the ISP.
*/
+#include <media/videobuf2-v4l2.h>
#include <type_support.h>
#include "ia_css_err.h"
#include "ia_css_types.h"
@@ -146,7 +147,18 @@ enum ia_css_frame_flash_state {
* This is the main structure used for all input and output images.
*/
struct ia_css_frame {
- struct ia_css_frame_info info; /** info struct describing the frame */
+ /*
+ * The videobuf2 core will allocate buffers including room for private
+ * data (the rest of struct ia_css_frame). The vb2_v4l2_buffer must
+ * be the first member for this to work!
+ * Note the atomisp code also uses ia_css_frame-s which are not used
+ * as v4l2-buffers in some places. In this case the vb2 member will
+ * be unused.
+ */
+ struct vb2_v4l2_buffer vb;
+ /* List-head for linking into the activeq or buffers_waiting_for_param list */
+ struct list_head queue;
+ struct ia_css_frame_info frame_info; /** info struct describing the frame */
ia_css_ptr data; /** pointer to start of image data */
unsigned int data_bytes; /** size of image data in bytes */
/* LA: move this to ia_css_buffer */
@@ -183,22 +195,16 @@ struct ia_css_frame {
info.format */
};
+#define vb_to_frame(vb2) \
+ container_of(to_vb2_v4l2_buffer(vb2), struct ia_css_frame, vb)
+
#define DEFAULT_FRAME { \
- .info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
.dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID, \
.buf_type = IA_CSS_BUFFER_TYPE_INVALID, \
.flash_state = IA_CSS_FRAME_FLASH_STATE_NONE, \
}
-/* @brief Fill a frame with zeros
- *
- * @param frame The frame.
- * @return None
- *
- * Fill a frame with pixel values of zero
- */
-void ia_css_frame_zero(struct ia_css_frame *frame);
-
/* @brief Allocate a CSS frame structure
*
* @param frame The allocated frame.
@@ -220,6 +226,17 @@ ia_css_frame_allocate(struct ia_css_frame **frame,
unsigned int stride,
unsigned int raw_bit_depth);
+/* @brief Initialize a CSS frame structure using a frame info structure.
+ *
+ * @param frame The allocated frame.
+ * @param[in] info The frame info structure.
+ * @return The error code.
+ *
+ * Initialize a frame using the resolution and format from a frame info struct.
+ */
+int ia_css_frame_init_from_info(struct ia_css_frame *frame,
+ const struct ia_css_frame_info *info);
+
/* @brief Allocate a CSS frame structure using a frame info structure.
*
* @param frame The allocated frame.
@@ -244,69 +261,10 @@ ia_css_frame_allocate_from_info(struct ia_css_frame **frame,
void
ia_css_frame_free(struct ia_css_frame *frame);
-/* @brief Allocate a CSS frame structure using a frame info structure.
- *
- * @param frame The allocated frame.
- * @param[in] info The frame info structure.
- * @return The error code.
- *
- * Allocate an empty CSS frame with no data buffer using the parameters
- * in the frame info.
- */
-int
-ia_css_frame_create_from_info(struct ia_css_frame **frame,
- const struct ia_css_frame_info *info);
-
-/* @brief Set a mapped data buffer to a CSS frame
- *
- * @param[in] frame Valid CSS frame pointer
- * @param[in] mapped_data Mapped data buffer to be assigned to the CSS frame
- * @param[in] data_size_bytes Size of the mapped_data in bytes
- * @return The error code.
- *
- * Sets a mapped data buffer to this frame. This function can be called multiple
- * times with different buffers or NULL to reset the data pointer. This API
- * would not try free the mapped_data and its the callers responsiblity to
- * free the mapped_data buffer. However if ia_css_frame_free() is called and
- * the frame had a valid data buffer, it would be freed along with the frame.
- */
-int
-ia_css_frame_set_data(struct ia_css_frame *frame,
- const ia_css_ptr mapped_data,
- size_t data_size_bytes);
-
-/* @brief Map an existing frame data pointer to a CSS frame.
- *
- * @param frame Pointer to the frame to be initialized
- * @param[in] info The frame info.
- * @param[in] data Pointer to the allocated frame data.
- * @param[in] attribute Attributes to be passed to mmgr_mmap.
- * @param[in] context Pointer to the a context to be passed to mmgr_mmap.
- * @return The allocated frame structure.
- *
- * This function maps a pre-allocated pointer into a CSS frame. This can be
- * used when an upper software layer is responsible for allocating the frame
- * data and it wants to share that frame pointer with the CSS code.
- * This function will fill the CSS frame structure just like
- * ia_css_frame_allocate() does, but instead of allocating the memory, it will
- * map the pre-allocated memory into the CSS address space.
- */
-int
-ia_css_frame_map(struct ia_css_frame **frame,
- const struct ia_css_frame_info *info,
- const void __user *data,
- unsigned int pgnr);
-
-/* @brief Unmap a CSS frame structure.
- *
- * @param[in] frame Pointer to the CSS frame.
- * @return None
- *
- * This function unmaps the frame data pointer within a CSS frame and
- * then frees the CSS frame structure. Use this for frame pointers created
- * using ia_css_frame_map().
- */
-void
-ia_css_frame_unmap(struct ia_css_frame *frame);
+static inline const struct ia_css_frame_info *
+ia_css_frame_get_info(const struct ia_css_frame *frame)
+{
+ return frame ? &frame->frame_info : NULL;
+}
#endif /* __IA_CSS_FRAME_PUBLIC_H */
diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/ia_css_pipe.h
index fb58535bff40..22522968b9e6 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_pipe.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_pipe.h
@@ -37,7 +37,6 @@ struct ia_css_preview_settings {
struct ia_css_pipe *copy_pipe;
struct ia_css_pipe *capture_pipe;
- struct ia_css_pipe *acc_pipe;
};
#define IA_CSS_DEFAULT_PREVIEW_SETTINGS { \
@@ -156,7 +155,7 @@ struct ia_css_pipe {
#define IA_CSS_DEFAULT_PIPE { \
.config = DEFAULT_PIPE_CONFIG, \
.info = DEFAULT_PIPE_INFO, \
- .mode = IA_CSS_PIPE_ID_ACC, /* (pipe_id) */ \
+ .mode = IA_CSS_PIPE_ID_VIDEO, /* (pipe_id) */ \
.pipeline = DEFAULT_PIPELINE, \
.output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
.bds_output_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h
index 7352cbf779fb..8ac1586dce4e 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h
@@ -45,7 +45,6 @@ enum ia_css_pipe_mode {
IA_CSS_PIPE_MODE_PREVIEW, /** Preview pipe */
IA_CSS_PIPE_MODE_VIDEO, /** Video pipe */
IA_CSS_PIPE_MODE_CAPTURE, /** Still capture pipe */
- IA_CSS_PIPE_MODE_ACC, /** Accelerated pipe */
IA_CSS_PIPE_MODE_COPY, /** Copy pipe, only used for embedded/image data copying */
IA_CSS_PIPE_MODE_YUVPP, /** YUV post processing pipe, used for all use cases with YUV input,
for SoC sensor and external ISP */
@@ -95,21 +94,11 @@ struct ia_css_pipe_config {
/** output of YUV scaling */
struct ia_css_frame_info vf_output_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE];
/** output of VF YUV scaling */
- struct ia_css_fw_info *acc_extension;
- /** Pipeline extension accelerator */
- struct ia_css_fw_info **acc_stages;
- /** Standalone accelerator stages */
- u32 num_acc_stages;
- /** Number of standalone accelerator stages */
struct ia_css_capture_config default_capture_config;
/** Default capture config for initial capture pipe configuration. */
struct ia_css_resolution dvs_envelope; /** temporary */
enum ia_css_frame_delay dvs_frame_delay;
/** indicates the DVS loop delay in frame periods */
- int acc_num_execs;
- /** For acceleration pipes only: determine how many times the pipe
- should be run. Setting this to -1 means it will run until
- stopped. */
bool enable_dz;
/** Disabling digital zoom for a pipeline, if this is set to false,
then setting a zoom factor will have no effect.
@@ -153,7 +142,6 @@ struct ia_css_pipe_config {
.vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
.default_capture_config = DEFAULT_CAPTURE_CONFIG, \
.dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \
- .acc_num_execs = -1, \
}
/* Pipe info, this struct describes properties of a pipe after it's stream has
@@ -224,9 +212,6 @@ struct ia_css_pipe_info {
{{0, 0}, 0, 0, 0, 0}, // second_output_info
{{0, 0}, 0, 0, 0, 0}, // vf_output_info
{{0, 0}, 0, 0, 0, 0}, // second_vf_output_info
- NULL, // acc_extension
- NULL, // acc_stages
- 0, // num_acc_stages
{
IA_CSS_CAPTURE_MODE_RAW, // mode
false, // enable_xnr
@@ -234,7 +219,6 @@ struct ia_css_pipe_info {
}, // default_capture_config
{0, 0}, // dvs_envelope
1, // dvs_frame_delay
- -1, // acc_num_execs
true, // enable_dz
NULL, // p_isp_config
};
@@ -426,59 +410,6 @@ int
ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
struct ia_css_buffer *buffer);
-/* @brief Set the state (Enable or Disable) of the Extension stage in the
- * given pipe.
- * @param[in] pipe Pipe handle.
- * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle)
- * @param[in] enable Enable Flag (1 to enable ; 0 to disable)
- *
- * @return
- * 0 : Success
- * -EINVAL : Invalid Parameters
- * -EBUSY : Inactive QOS Pipe
- * (No active stream with this pipe)
- *
- * This function will request state change (enable or disable) for the Extension
- * stage (firmware handle) in the given pipe.
- *
- * Note:
- * 1. Extension can be enabled/disabled only on QOS Extensions
- * 2. Extension can be enabled/disabled only with an active QOS Pipe
- * 3. Initial(Default) state of QOS Extensions is Disabled
- * 4. State change cannot be guaranteed immediately OR on frame boundary
- *
- */
-int
-ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe,
- u32 fw_handle,
- bool enable);
-
-/* @brief Get the state (Enable or Disable) of the Extension stage in the
- * given pipe.
- * @param[in] pipe Pipe handle.
- * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle)
- * @param[out] *enable Enable Flag
- *
- * @return
- * 0 : Success
- * -EINVAL : Invalid Parameters
- * -EBUSY : Inactive QOS Pipe
- * (No active stream with this pipe)
- *
- * This function will query the state of the Extension stage (firmware handle)
- * in the given Pipe.
- *
- * Note:
- * 1. Extension state can be queried only on QOS Extensions
- * 2. Extension can be enabled/disabled only with an active QOS Pipe
- * 3. Initial(Default) state of QOS Extensions is Disabled.
- *
- */
-int
-ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe,
- u32 fw_handle,
- bool *enable);
-
/* @brief Get selected configuration settings
* @param[in] pipe The pipe.
* @param[out] config Configuration settings.
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c
index c7d88552dfde..0091e2a3da52 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c
@@ -28,9 +28,7 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary,
const struct ia_css_frame *in_frame = args->in_frame;
const struct ia_css_frame **out_frames = (const struct ia_css_frame **)
&args->out_frame;
- const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info :
- &binary->in_frame_info;
-
+ const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame);
const unsigned int ddr_bits_per_element = sizeof(short) * 8;
const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS,
ddr_bits_per_element);
@@ -80,12 +78,12 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary,
"ia_css_bayer_io_config() put part enter:\n");
#endif
- ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info);
+ ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info);
if (ret)
return ret;
to->base_address = out_frames[0]->data;
- to->width = out_frames[0]->info.res.width;
- to->height = out_frames[0]->info.res.height;
+ to->width = out_frames[0]->frame_info.res.width;
+ to->height = out_frames[0]->frame_info.res.height;
to->stride = config.stride;
to->ddr_elems_per_word = ddr_elems_per_word;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c
index 7d2ef6e26ee6..32c504a950ce 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c
@@ -28,9 +28,7 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary,
const struct ia_css_frame *in_frame = args->in_frame;
const struct ia_css_frame **out_frames = (const struct ia_css_frame **)
&args->out_frame;
- const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info :
- &binary->in_frame_info;
-
+ const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame);
const unsigned int ddr_bits_per_element = sizeof(short) * 8;
const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS,
ddr_bits_per_element);
@@ -81,13 +79,13 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary,
"ia_css_yuv444_io_config() put part enter:\n");
#endif
- ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info);
+ ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info);
if (ret)
return ret;
to->base_address = out_frames[0]->data;
- to->width = out_frames[0]->info.res.width;
- to->height = out_frames[0]->info.res.height;
+ to->width = out_frames[0]->frame_info.res.width;
+ to->height = out_frames[0]->frame_info.res.height;
to->stride = config.stride;
to->ddr_elems_per_word = ddr_elems_per_word;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c
index 08ed916a7eb8..9288a7a37b37 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c
@@ -30,7 +30,7 @@ int ia_css_ref_config(struct sh_css_isp_ref_isp_config *to,
int ret;
if (from->ref_frames[0]) {
- ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->info);
+ ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->frame_info);
if (ret)
return ret;
to->width_a_over_b = elems_a / to->port_b.elems;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
index 53050c0c49fc..a5fea753ec64 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c
@@ -79,11 +79,11 @@ int ia_css_tnr_config(struct sh_css_isp_tnr_isp_config *to,
unsigned int i;
int ret;
- ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->info);
+ ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->frame_info);
if (ret)
return ret;
to->width_a_over_b = elems_a / to->port_b.elems;
- to->frame_height = from->tnr_frames[0]->info.res.height;
+ to->frame_height = from->tnr_frames[0]->frame_info.res.height;
for (i = 0; i < NUM_VIDEO_TNR_FRAMES; i++) {
to->tnr_frame_addr[i] = from->tnr_frames[i]->data +
from->tnr_frames[i]->planes.yuyv.offset;
diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
index 406ed5fb4c6a..768da86b8c2c 100644
--- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
+++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
@@ -13,6 +13,8 @@
* more details.
*/
+#include <linux/math.h>
+
#include <math_support.h>
#include <gdc_device.h> /* HR_GDC_N */
@@ -128,16 +130,8 @@ ia_css_binary_compute_shading_table_bayer_origin(
{
int err;
- /* Numerator and denominator of the fixed bayer downscaling factor.
- (numerator >= denominator) */
- unsigned int bds_num, bds_den;
-
- /* Horizontal/Vertical ratio of bayer scaling
- between input area and output area. */
- unsigned int bs_hor_ratio_in;
- unsigned int bs_hor_ratio_out;
- unsigned int bs_ver_ratio_in;
- unsigned int bs_ver_ratio_out;
+ /* Rational fraction of the fixed bayer downscaling factor. */
+ struct u32_fract bds;
/* Left padding set by InputFormatter. */
unsigned int left_padding_bqs; /* in bqs */
@@ -158,19 +152,11 @@ ia_css_binary_compute_shading_table_bayer_origin(
unsigned int bad_bqs_on_top_before_bs; /* in bqs */
unsigned int bad_bqs_on_top_after_bs; /* in bqs */
- /* Get the numerator and denominator of bayer downscaling factor. */
- err = sh_css_bds_factor_get_numerator_denominator
- (required_bds_factor, &bds_num, &bds_den);
+ /* Get the rational fraction of bayer downscaling factor. */
+ err = sh_css_bds_factor_get_fract(required_bds_factor, &bds);
if (err)
return err;
- /* Set the horizontal/vertical ratio of bayer scaling
- between input area and output area. */
- bs_hor_ratio_in = bds_num;
- bs_hor_ratio_out = bds_den;
- bs_ver_ratio_in = bds_num;
- bs_ver_ratio_out = bds_den;
-
/* Set the left padding set by InputFormatter. (ifmtr.c) */
if (stream_config->left_padding == -1)
left_padding_bqs = _ISP_BQS(binary->left_padding);
@@ -228,18 +214,18 @@ ia_css_binary_compute_shading_table_bayer_origin(
located on the shading table during the shading correction. */
res->sc_bayer_origin_x_bqs_on_shading_table =
((left_padding_adjusted_bqs + bad_bqs_on_left_before_bs)
- * bs_hor_ratio_out + bs_hor_ratio_in / 2) / bs_hor_ratio_in
+ * bds.denominator + bds.numerator / 2) / bds.numerator
+ bad_bqs_on_left_after_bs;
- /* "+ bs_hor_ratio_in/2": rounding for division by bs_hor_ratio_in */
+ /* "+ bds.numerator / 2": rounding for division by bds.numerator */
res->sc_bayer_origin_y_bqs_on_shading_table =
- (bad_bqs_on_top_before_bs * bs_ver_ratio_out + bs_ver_ratio_in / 2) / bs_ver_ratio_in
+ (bad_bqs_on_top_before_bs * bds.denominator + bds.numerator / 2) / bds.numerator
+ bad_bqs_on_top_after_bs;
- /* "+ bs_ver_ratio_in/2": rounding for division by bs_ver_ratio_in */
+ /* "+ bds.numerator / 2": rounding for division by bds.numerator */
- res->bayer_scale_hor_ratio_in = (uint32_t)bs_hor_ratio_in;
- res->bayer_scale_hor_ratio_out = (uint32_t)bs_hor_ratio_out;
- res->bayer_scale_ver_ratio_in = (uint32_t)bs_ver_ratio_in;
- res->bayer_scale_ver_ratio_out = (uint32_t)bs_ver_ratio_out;
+ res->bayer_scale_hor_ratio_in = bds.numerator;
+ res->bayer_scale_hor_ratio_out = bds.denominator;
+ res->bayer_scale_ver_ratio_in = bds.numerator;
+ res->bayer_scale_ver_ratio_out = bds.denominator;
return err;
}
diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
index 3d269bd23207..bb6204cb42c5 100644
--- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
+++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
@@ -133,7 +133,6 @@ static const char *const pipe_id_to_str[] = {
/* [IA_CSS_PIPE_ID_VIDEO] =*/ "video",
/* [IA_CSS_PIPE_ID_CAPTURE] =*/ "capture",
/* [IA_CSS_PIPE_ID_YUVPP] =*/ "yuvpp",
- /* [IA_CSS_PIPE_ID_ACC] =*/ "accelerator"
};
static char dot_id_input_bin[SH_CSS_MAX_BINARY_NAME + 10];
@@ -1301,11 +1300,11 @@ void ia_css_debug_frame_print(const struct ia_css_frame *frame,
data = (char *)HOST_ADDRESS(frame->data);
ia_css_debug_dtrace(2, "frame %s (%p):\n", descr, frame);
ia_css_debug_dtrace(2, " resolution = %dx%d\n",
- frame->info.res.width, frame->info.res.height);
+ frame->frame_info.res.width, frame->frame_info.res.height);
ia_css_debug_dtrace(2, " padded width = %d\n",
- frame->info.padded_width);
- ia_css_debug_dtrace(2, " format = %d\n", frame->info.format);
- switch (frame->info.format) {
+ frame->frame_info.padded_width);
+ ia_css_debug_dtrace(2, " format = %d\n", frame->frame_info.format);
+ switch (frame->frame_info.format) {
case IA_CSS_FRAME_FORMAT_NV12:
case IA_CSS_FRAME_FORMAT_NV16:
case IA_CSS_FRAME_FORMAT_NV21:
@@ -2565,11 +2564,11 @@ ia_css_debug_pipe_graph_dump_frame(
dtrace_dot(
"node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d, %dbpp\\n%s\"];",
frame,
- debug_frame_format2str(frame->info.format),
- frame->info.res.width,
- frame->info.padded_width,
- frame->info.res.height,
- frame->info.raw_bit_depth,
+ debug_frame_format2str(frame->frame_info.format),
+ frame->frame_info.res.width,
+ frame->frame_info.padded_width,
+ frame->frame_info.res.height,
+ frame->frame_info.raw_bit_depth,
bufinfo);
if (in_frame) {
@@ -2866,10 +2865,10 @@ ia_css_debug_pipe_graph_dump_sp_raw_copy(
snprintf(ring_buffer, sizeof(ring_buffer),
"node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d\\nRingbuffer\"];",
out_frame,
- debug_frame_format2str(out_frame->info.format),
- out_frame->info.res.width,
- out_frame->info.padded_width,
- out_frame->info.res.height);
+ debug_frame_format2str(out_frame->frame_info.format),
+ out_frame->frame_info.res.width,
+ out_frame->frame_info.padded_width,
+ out_frame->frame_info.res.height);
dtrace_dot(ring_buffer);
@@ -2989,16 +2988,10 @@ ia_css_debug_dump_pipe_config(
ia_css_debug_dump_frame_info(&config->vf_output_info[i],
"vf_output_info");
}
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: %p\n",
- config->acc_extension);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "num_acc_stages: %d\n",
- config->num_acc_stages);
ia_css_debug_dump_capture_config(&config->default_capture_config);
ia_css_debug_dump_resolution(&config->dvs_envelope, "dvs_envelope");
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "dvs_frame_delay: %d\n",
config->dvs_frame_delay);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_num_execs: %d\n",
- config->acc_num_execs);
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "enable_dz: %d\n",
config->enable_dz);
IA_CSS_LEAVE_PRIVATE("");
diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
index 5a7058320ee6..83bb42e05421 100644
--- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
+++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
@@ -88,12 +88,6 @@ ia_css_elems_bytes_from_info(
** CSS API functions, exposed by ia_css.h
**************************************************************************/
-void ia_css_frame_zero(struct ia_css_frame *frame)
-{
- assert(frame);
- hmm_set(frame->data, 0, frame->data_bytes);
-}
-
int ia_css_frame_allocate_from_info(struct ia_css_frame **frame,
const struct ia_css_frame_info *info)
{
@@ -143,121 +137,6 @@ int ia_css_frame_allocate(struct ia_css_frame **frame,
return err;
}
-int ia_css_frame_map(struct ia_css_frame **frame,
- const struct ia_css_frame_info *info,
- const void __user *data,
- unsigned int pgnr)
-{
- int err = 0;
- struct ia_css_frame *me;
-
- assert(frame);
-
- /* Create the frame structure */
- err = ia_css_frame_create_from_info(&me, info);
-
- if (err)
- return err;
-
- if (pgnr < ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) {
- dev_err(atomisp_dev,
- "user space memory size is less than the expected size..\n");
- err = -ENOMEM;
- goto error;
- } else if (pgnr > ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) {
- dev_err(atomisp_dev,
- "user space memory size is large than the expected size..\n");
- err = -ENOMEM;
- goto error;
- }
-
- me->data = hmm_create_from_userdata(me->data_bytes, data);
- if (me->data == mmgr_NULL)
- err = -EINVAL;
-
-error:
- if (err) {
- kvfree(me);
- me = NULL;
- }
-
- *frame = me;
-
- return err;
-}
-
-int ia_css_frame_create_from_info(struct ia_css_frame **frame,
- const struct ia_css_frame_info *info)
-{
- int err = 0;
- struct ia_css_frame *me;
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_create_from_info() enter:\n");
- if (!frame || !info) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_create_from_info() leave: invalid arguments\n");
- return -EINVAL;
- }
-
- me = frame_create(info->res.width,
- info->res.height,
- info->format,
- info->padded_width,
- info->raw_bit_depth,
- false);
- if (!me) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_create_from_info() leave: frame create failed\n");
- return -ENOMEM;
- }
-
- err = ia_css_frame_init_planes(me);
-
- if (err) {
- kvfree(me);
- me = NULL;
- }
-
- *frame = me;
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_create_from_info() leave:\n");
-
- return err;
-}
-
-int ia_css_frame_set_data(struct ia_css_frame *frame,
- const ia_css_ptr mapped_data,
- size_t data_bytes)
-{
- int err = 0;
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_set_data() enter:\n");
- if (!frame) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_set_data() leave: NULL frame\n");
- return -EINVAL;
- }
-
- /* If we are setting a valid data.
- * Make sure that there is enough
- * room for the expected frame format
- */
- if ((mapped_data != mmgr_NULL) && (frame->data_bytes > data_bytes)) {
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "ia_css_frame_set_data() leave: invalid arguments\n");
- return -EINVAL;
- }
-
- frame->data = mapped_data;
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_set_data() leave:\n");
-
- return err;
-}
-
void ia_css_frame_free(struct ia_css_frame *frame)
{
IA_CSS_ENTER_PRIVATE("frame = %p", frame);
@@ -286,32 +165,32 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame)
{
assert(frame);
- switch (frame->info.format) {
+ switch (frame->frame_info.format) {
case IA_CSS_FRAME_FORMAT_MIPI:
dev_err(atomisp_dev,
"%s: unexpected use of IA_CSS_FRAME_FORMAT_MIPI\n", __func__);
return -EINVAL;
case IA_CSS_FRAME_FORMAT_RAW_PACKED:
frame_init_raw_single_plane(frame, &frame->planes.raw,
- frame->info.res.height,
- frame->info.padded_width,
- frame->info.raw_bit_depth);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width,
+ frame->frame_info.raw_bit_depth);
break;
case IA_CSS_FRAME_FORMAT_RAW:
frame_init_single_plane(frame, &frame->planes.raw,
- frame->info.res.height,
- frame->info.padded_width,
- frame->info.raw_bit_depth <= 8 ? 1 : 2);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width,
+ frame->frame_info.raw_bit_depth <= 8 ? 1 : 2);
break;
case IA_CSS_FRAME_FORMAT_RGB565:
frame_init_single_plane(frame, &frame->planes.rgb,
- frame->info.res.height,
- frame->info.padded_width, 2);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width, 2);
break;
case IA_CSS_FRAME_FORMAT_RGBA888:
frame_init_single_plane(frame, &frame->planes.rgb,
- frame->info.res.height,
- frame->info.padded_width * 4, 1);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width * 4, 1);
break;
case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
frame_init_rgb_planes(frame, 1);
@@ -324,14 +203,14 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame)
case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
frame_init_single_plane(frame, &frame->planes.yuyv,
- frame->info.res.height,
- frame->info.padded_width * 2, 1);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width * 2, 1);
break;
case IA_CSS_FRAME_FORMAT_YUV_LINE:
/* Needs 3 extra lines to allow vf_pp prefetching */
frame_init_single_plane(frame, &frame->planes.yuyv,
- frame->info.res.height * 3 / 2 + 3,
- frame->info.padded_width, 1);
+ frame->frame_info.res.height * 3 / 2 + 3,
+ frame->frame_info.padded_width, 1);
break;
case IA_CSS_FRAME_FORMAT_NV11:
frame_init_nv_planes(frame, 4, 1, 1);
@@ -380,8 +259,8 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame)
break;
case IA_CSS_FRAME_FORMAT_BINARY_8:
frame_init_single_plane(frame, &frame->planes.binary.data,
- frame->info.res.height,
- frame->info.padded_width, 1);
+ frame->frame_info.res.height,
+ frame->frame_info.padded_width, 1);
frame->planes.binary.size = 0;
break;
default:
@@ -510,8 +389,8 @@ bool ia_css_frame_is_same_type(const struct ia_css_frame *frame_a,
const struct ia_css_frame *frame_b)
{
bool is_equal = false;
- const struct ia_css_frame_info *info_a = &frame_a->info,
- *info_b = &frame_b->info;
+ const struct ia_css_frame_info *info_a = &frame_a->frame_info;
+ const struct ia_css_frame_info *info_b = &frame_b->frame_info;
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
"ia_css_frame_is_same_type() enter:\n");
@@ -613,8 +492,8 @@ static void frame_init_nv_planes(struct ia_css_frame *frame,
unsigned int vertical_decimation,
unsigned int bytes_per_element)
{
- unsigned int y_width = frame->info.padded_width;
- unsigned int y_height = frame->info.res.height;
+ unsigned int y_width = frame->frame_info.padded_width;
+ unsigned int y_height = frame->frame_info.res.height;
unsigned int uv_width;
unsigned int uv_height;
unsigned int y_bytes;
@@ -627,7 +506,7 @@ static void frame_init_nv_planes(struct ia_css_frame *frame,
uv_width = 2 * (y_width / horizontal_decimation);
uv_height = y_height / vertical_decimation;
- if (frame->info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) {
+ if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) {
y_width = CEIL_MUL(y_width, NV12_TILEY_TILE_WIDTH);
uv_width = CEIL_MUL(uv_width, NV12_TILEY_TILE_WIDTH);
y_height = CEIL_MUL(y_height, NV12_TILEY_TILE_HEIGHT);
@@ -652,8 +531,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame,
bool swap_uv,
unsigned int bytes_per_element)
{
- unsigned int y_width = frame->info.padded_width,
- y_height = frame->info.res.height,
+ unsigned int y_width = frame->frame_info.padded_width,
+ y_height = frame->frame_info.res.height,
uv_width = y_width / horizontal_decimation,
uv_height = y_height / vertical_decimation,
y_stride, y_bytes, uv_bytes, uv_stride;
@@ -682,8 +561,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame,
static void frame_init_rgb_planes(struct ia_css_frame *frame,
unsigned int bytes_per_element)
{
- unsigned int width = frame->info.res.width,
- height = frame->info.res.height, stride, bytes;
+ unsigned int width = frame->frame_info.res.width,
+ height = frame->frame_info.res.height, stride, bytes;
stride = width * bytes_per_element;
bytes = stride * height;
@@ -698,8 +577,8 @@ static void frame_init_rgb_planes(struct ia_css_frame *frame,
static void frame_init_qplane6_planes(struct ia_css_frame *frame)
{
- unsigned int width = frame->info.padded_width / 2,
- height = frame->info.res.height / 2, bytes, stride;
+ unsigned int width = frame->frame_info.padded_width / 2,
+ height = frame->frame_info.res.height / 2, bytes, stride;
stride = width * 2;
bytes = stride * height;
@@ -781,11 +660,11 @@ static struct ia_css_frame *frame_create(unsigned int width,
return NULL;
memset(me, 0, sizeof(*me));
- me->info.res.width = width;
- me->info.res.height = height;
- me->info.format = format;
- me->info.padded_width = padded_width;
- me->info.raw_bit_depth = raw_bit_depth;
+ me->frame_info.res.width = width;
+ me->frame_info.res.height = height;
+ me->frame_info.format = format;
+ me->frame_info.padded_width = padded_width;
+ me->frame_info.raw_bit_depth = raw_bit_depth;
me->valid = valid;
me->data_bytes = 0;
me->data = mmgr_NULL;
@@ -847,3 +726,19 @@ void ia_css_resolution_to_sp_resolution(
to->width = (uint16_t)from->width;
to->height = (uint16_t)from->height;
}
+
+int ia_css_frame_init_from_info(struct ia_css_frame *frame,
+ const struct ia_css_frame_info *frame_info)
+{
+ frame->frame_info.res.width = frame_info->res.width;
+ frame->frame_info.res.height = frame_info->res.height;
+ frame->frame_info.format = frame_info->format;
+ frame->frame_info.padded_width = frame_info->padded_width;
+ frame->frame_info.raw_bit_depth = frame_info->raw_bit_depth;
+ frame->valid = true;
+ /* To indicate it is not valid frame. */
+ frame->dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID;
+ frame->buf_type = IA_CSS_BUFFER_TYPE_INVALID;
+
+ return ia_css_frame_init_planes(frame);
+}
diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
index de2c526a58ae..222c381ff3b9 100644
--- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
+++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
@@ -54,7 +54,6 @@ struct ia_css_pipeline {
unsigned int inout_port_config;
int num_execs;
bool acquire_isp_each_stage;
- u32 pipe_qos_config;
};
#define DEFAULT_PIPELINE { \
@@ -65,7 +64,6 @@ struct ia_css_pipeline {
.dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \
.num_execs = -1, \
.acquire_isp_each_stage = true, \
- .pipe_qos_config = QOS_INVALID \
}
/* Stage descriptor used to create a new stage in the pipeline */
diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
index dfc50247ea8e..e9e187649a65 100644
--- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
+++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
@@ -774,14 +774,6 @@ ia_css_pipeline_configure_inout_port(struct ia_css_pipeline *me,
(uint8_t)SH_CSS_PORT_OUTPUT,
(uint8_t)SH_CSS_HOST_TYPE, 1);
break;
- case IA_CSS_PIPE_ID_ACC:
- SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config,
- (uint8_t)SH_CSS_PORT_INPUT,
- (uint8_t)SH_CSS_HOST_TYPE, 1);
- SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config,
- (uint8_t)SH_CSS_PORT_OUTPUT,
- (uint8_t)SH_CSS_HOST_TYPE, 1);
- break;
default:
break;
}
diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c
index da96aaffebc1..726cb7aa4ecd 100644
--- a/drivers/staging/media/atomisp/pci/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/sh_css.c
@@ -209,13 +209,6 @@ ia_css_pipe_check_format(struct ia_css_pipe *pipe,
enum ia_css_frame_format format);
/* ISP 2401 */
-static int
-ia_css_pipe_load_extension(struct ia_css_pipe *pipe,
- struct ia_css_fw_info *firmware);
-
-static void
-ia_css_pipe_unload_extension(struct ia_css_pipe *pipe,
- struct ia_css_fw_info *firmware);
static void
ia_css_reset_defaults(struct sh_css *css);
@@ -287,10 +280,6 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe,
struct ia_css_frame *out_frame, unsigned int idx);
static int
-sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline,
- const void *acc_fw);
-
-static int
alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time);
static void
@@ -329,9 +318,6 @@ create_host_capture_pipeline(struct ia_css_pipe *pipe);
static int
create_host_yuvpp_pipeline(struct ia_css_pipe *pipe);
-static int
-create_host_acc_pipeline(struct ia_css_pipe *pipe);
-
static unsigned int
sh_css_get_sw_interrupt_value(unsigned int irq);
@@ -362,12 +348,6 @@ static struct sh_css_hmm_buffer_record
*sh_css_hmm_buffer_record_validate(ia_css_ptr ddr_buffer_addr,
enum ia_css_buffer_type type);
-void
-ia_css_get_acc_configs(
- struct ia_css_pipe *pipe,
- struct ia_css_isp_config *config);
-
-
#ifdef ISP2401
static unsigned int get_crop_lines_for_bayer_order(const struct
ia_css_stream_config *config);
@@ -1649,15 +1629,6 @@ ia_css_enable_isys_event_queue(bool enable)
return 0;
}
-/* For Acceleration API: Flush FW (shared buffer pointer) arguments */
-void
-sh_css_flush(struct ia_css_acc_fw *fw)
-{
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_flush() enter:\n");
- if ((fw) && (my_css.flush))
- my_css.flush(fw);
-}
-
/*
* Mapping sp threads. Currently, this is done when a stream is created and
* pipelines are ready to be converted to sp pipelines. Be careful if you are
@@ -1670,7 +1641,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map)
struct ia_css_pipe *main_pipe = NULL;
struct ia_css_pipe *copy_pipe = NULL;
struct ia_css_pipe *capture_pipe = NULL;
- struct ia_css_pipe *acc_pipe = NULL;
int err = 0;
enum ia_css_pipe_id pipe_id;
@@ -1691,7 +1661,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map)
case IA_CSS_PIPE_ID_PREVIEW:
copy_pipe = main_pipe->pipe_settings.preview.copy_pipe;
capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
- acc_pipe = main_pipe->pipe_settings.preview.acc_pipe;
break;
case IA_CSS_PIPE_ID_VIDEO:
@@ -1700,14 +1669,10 @@ map_sp_threads(struct ia_css_stream *stream, bool map)
break;
case IA_CSS_PIPE_ID_CAPTURE:
- case IA_CSS_PIPE_ID_ACC:
default:
break;
}
- if (acc_pipe)
- ia_css_pipeline_map(acc_pipe->pipe_num, map);
-
if (capture_pipe)
ia_css_pipeline_map(capture_pipe->pipe_num, map);
@@ -1735,7 +1700,6 @@ static int
create_host_pipeline_structure(struct ia_css_stream *stream)
{
struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL;
- struct ia_css_pipe *acc_pipe = NULL;
enum ia_css_pipe_id pipe_id;
struct ia_css_pipe *main_pipe = NULL;
int err = 0;
@@ -1763,7 +1727,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream)
copy_pipe_delay = main_pipe->dvs_frame_delay;
capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
capture_pipe_delay = IA_CSS_FRAME_DELAY_0;
- acc_pipe = main_pipe->pipe_settings.preview.acc_pipe;
err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
main_pipe->pipe_num, main_pipe->dvs_frame_delay);
break;
@@ -1787,11 +1750,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream)
main_pipe->pipe_num, main_pipe->dvs_frame_delay);
break;
- case IA_CSS_PIPE_ID_ACC:
- err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
- main_pipe->pipe_num, main_pipe->dvs_frame_delay);
- break;
-
default:
err = -EINVAL;
}
@@ -1808,10 +1766,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream)
capture_pipe->pipe_num,
capture_pipe_delay);
- if (!(err) && acc_pipe)
- err = ia_css_pipeline_create(&acc_pipe->pipeline, acc_pipe->mode,
- acc_pipe->pipe_num, main_pipe->dvs_frame_delay);
-
/* DH regular multi pipe - not continuous mode: create the next pipelines too */
if (!stream->config.continuous) {
int i;
@@ -1837,7 +1791,6 @@ static int
create_host_pipeline(struct ia_css_stream *stream)
{
struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL;
- struct ia_css_pipe *acc_pipe = NULL;
enum ia_css_pipe_id pipe_id;
struct ia_css_pipe *main_pipe = NULL;
int err = 0;
@@ -1881,27 +1834,17 @@ create_host_pipeline(struct ia_css_stream *stream)
}
}
-#if !defined(ISP2401)
/* old isys: need to allocate_mipi_frames() even in IA_CSS_PIPE_MODE_COPY */
- if (pipe_id != IA_CSS_PIPE_ID_ACC) {
- err = allocate_mipi_frames(main_pipe, &stream->info);
- if (err)
- goto ERR;
- }
-#elif defined(ISP2401)
- if ((pipe_id != IA_CSS_PIPE_ID_ACC) &&
- (main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY)) {
+ if (!IS_ISP2401 || main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY) {
err = allocate_mipi_frames(main_pipe, &stream->info);
if (err)
goto ERR;
}
-#endif
switch (pipe_id) {
case IA_CSS_PIPE_ID_PREVIEW:
copy_pipe = main_pipe->pipe_settings.preview.copy_pipe;
capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
- acc_pipe = main_pipe->pipe_settings.preview.acc_pipe;
max_input_width =
main_pipe->pipe_settings.preview.preview_binary.info->sp.input.max_width;
@@ -1935,12 +1878,6 @@ create_host_pipeline(struct ia_css_stream *stream)
break;
- case IA_CSS_PIPE_ID_ACC:
- err = create_host_acc_pipeline(main_pipe);
- if (err)
- goto ERR;
-
- break;
default:
err = -EINVAL;
}
@@ -1960,12 +1897,6 @@ create_host_pipeline(struct ia_css_stream *stream)
goto ERR;
}
- if (acc_pipe) {
- err = create_host_acc_pipeline(acc_pipe);
- if (err)
- goto ERR;
- }
-
/* DH regular multi pipe - not continuous mode: create the next pipelines too */
if (!stream->config.continuous) {
int i;
@@ -1984,9 +1915,6 @@ create_host_pipeline(struct ia_css_stream *stream)
case IA_CSS_PIPE_ID_YUVPP:
err = create_host_yuvpp_pipeline(stream->pipes[i]);
break;
- case IA_CSS_PIPE_ID_ACC:
- err = create_host_acc_pipeline(stream->pipes[i]);
- break;
default:
err = -EINVAL;
}
@@ -2037,9 +1965,6 @@ init_pipe_defaults(enum ia_css_pipe_mode mode,
pipe->mode = IA_CSS_PIPE_ID_VIDEO;
memcpy(&pipe->pipe_settings.video, &video, sizeof(video));
break;
- case IA_CSS_PIPE_MODE_ACC:
- pipe->mode = IA_CSS_PIPE_ID_ACC;
- break;
case IA_CSS_PIPE_MODE_COPY:
pipe->mode = IA_CSS_PIPE_ID_CAPTURE;
break;
@@ -2156,27 +2081,6 @@ find_pipe_by_num(uint32_t pipe_num)
return NULL;
}
-static void sh_css_pipe_free_acc_binaries(
- struct ia_css_pipe *pipe)
-{
- struct ia_css_pipeline *pipeline;
- struct ia_css_pipeline_stage *stage;
-
- if (!pipe) {
- IA_CSS_ERROR("NULL input pointer");
- return;
- }
- pipeline = &pipe->pipeline;
-
- /* loop through the stages and unload them */
- for (stage = pipeline->stages; stage; stage = stage->next) {
- struct ia_css_fw_info *firmware = (struct ia_css_fw_info *)
- stage->firmware;
- if (firmware)
- ia_css_pipe_unload_extension(pipe, firmware);
- }
-}
-
int
ia_css_pipe_destroy(struct ia_css_pipe *pipe)
{
@@ -2241,9 +2145,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe)
ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES,
pipe->pipe_settings.capture.delay_frames);
break;
- case IA_CSS_PIPE_MODE_ACC:
- sh_css_pipe_free_acc_binaries(pipe);
- break;
case IA_CSS_PIPE_MODE_COPY:
break;
case IA_CSS_PIPE_MODE_YUVPP:
@@ -2261,10 +2162,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe)
ia_css_pipeline_destroy(&pipe->pipeline);
pipe_release_pipe_num(ia_css_pipe_get_pipe_num(pipe));
- /* Temporarily, not every sh_css_pipe has an acc_extension. */
- if (pipe->config.acc_extension)
- ia_css_pipe_unload_extension(pipe, pipe->config.acc_extension);
-
kfree(pipe);
IA_CSS_LEAVE("err = %d", err);
return err;
@@ -3060,7 +2957,7 @@ init_vf_frameinfo_defaults(struct ia_css_pipe *pipe,
assert(vf_frame);
- sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->info, idx);
+ sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->frame_info, idx);
vf_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx, thread_id, &queue_id);
@@ -3229,31 +3126,31 @@ init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe,
assert(frame);
in_frame = frame;
- in_frame->info.format = format;
+ in_frame->frame_info.format = format;
#ifdef ISP2401
if (format == IA_CSS_FRAME_FORMAT_RAW)
- in_frame->info.format = (pipe->stream->config.pack_raw_pixels) ?
+ in_frame->frame_info.format = (pipe->stream->config.pack_raw_pixels) ?
IA_CSS_FRAME_FORMAT_RAW_PACKED : IA_CSS_FRAME_FORMAT_RAW;
#endif
- in_frame->info.res.width = pipe->stream->config.input_config.input_res.width;
- in_frame->info.res.height = pipe->stream->config.input_config.input_res.height;
- in_frame->info.raw_bit_depth =
- ia_css_pipe_util_pipe_input_format_bpp(pipe);
- ia_css_frame_info_set_width(&in_frame->info, pipe->stream->config.input_config.input_res.width, 0);
+ in_frame->frame_info.res.width = pipe->stream->config.input_config.input_res.width;
+ in_frame->frame_info.res.height = pipe->stream->config.input_config.input_res.height;
+ in_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe);
+ ia_css_frame_info_set_width(&in_frame->frame_info,
+ pipe->stream->config.input_config.input_res.width, 0);
in_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_INPUT_FRAME, thread_id, &queue_id);
in_frame->dynamic_queue_id = queue_id;
in_frame->buf_type = IA_CSS_BUFFER_TYPE_INPUT_FRAME;
#ifdef ISP2401
- ia_css_get_crop_offsets(pipe, &in_frame->info);
+ ia_css_get_crop_offsets(pipe, &in_frame->frame_info);
#endif
err = ia_css_frame_init_planes(in_frame);
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
- "init_in_frameinfo_memory_defaults() bayer_order = %d:\n", in_frame->info.raw_bayer_order);
+ ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "%s() bayer_order = %d\n",
+ __func__, in_frame->frame_info.raw_bayer_order);
return err;
}
@@ -3268,7 +3165,7 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe,
assert(out_frame);
- sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, idx);
+ sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, idx);
out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx, thread_id, &queue_id);
@@ -3433,31 +3330,6 @@ static int create_host_video_pipeline(struct ia_css_pipe *pipe)
}
}
- /* Append Extension on Video out, if enabled */
- if (!need_vf_pp && video_stage && pipe->config.acc_extension &&
- (pipe->config.acc_extension->info.isp.type == IA_CSS_ACC_OUTPUT)) {
- struct ia_css_frame *out = NULL;
- struct ia_css_frame *in = NULL;
-
- if ((pipe->config.acc_extension->info.isp.sp.enable.output) &&
- (pipe->config.acc_extension->info.isp.sp.enable.in_frame) &&
- (pipe->config.acc_extension->info.isp.sp.enable.out_frame)) {
- /* In/Out Frame mapping to support output frame extension.*/
- out = video_stage->args.out_frame[0];
- err = ia_css_frame_allocate_from_info(&in, &pipe->output_info[0]);
- if (err)
- goto ERR;
- video_stage->args.out_frame[0] = in;
- }
-
- err = add_firmwares(me, video_binary, pipe->output_stage,
- last_output_firmware(pipe->output_stage),
- IA_CSS_BINARY_MODE_VIDEO,
- in, out, NULL, &video_stage, NULL);
- if (err)
- goto ERR;
- }
-
if (need_yuv_pp && video_stage) {
struct ia_css_frame *tmp_in_frame = video_stage->args.out_frame[0];
struct ia_css_frame *tmp_out_frame = NULL;
@@ -3489,45 +3361,6 @@ ERR:
return err;
}
-static int
-create_host_acc_pipeline(struct ia_css_pipe *pipe)
-{
- int err = 0;
- const struct ia_css_fw_info *fw;
- unsigned int i;
-
- IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
- if ((!pipe) || (!pipe->stream)) {
- IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
- return -EINVAL;
- }
-
- pipe->pipeline.num_execs = pipe->config.acc_num_execs;
- /* Reset pipe_qos_config to default disable all QOS extension stages */
- if (pipe->config.acc_extension)
- pipe->pipeline.pipe_qos_config = 0;
-
- for (fw = pipe->vf_stage; fw; fw = fw->next) {
- err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw);
- if (err)
- goto ERR;
- }
-
- for (i = 0; i < pipe->config.num_acc_stages; i++) {
- struct ia_css_fw_info *fw = pipe->config.acc_stages[i];
-
- err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw);
- if (err)
- goto ERR;
- }
-
- ia_css_pipeline_finalize_stages(&pipe->pipeline, pipe->stream->config.continuous);
-
-ERR:
- IA_CSS_LEAVE_ERR_PRIVATE(err);
- return err;
-}
-
/* Create stages for preview */
static int
create_host_preview_pipeline(struct ia_css_pipe *pipe)
@@ -3690,7 +3523,6 @@ preview_start(struct ia_css_pipe *pipe)
{
int err = 0;
struct ia_css_pipe *copy_pipe, *capture_pipe;
- struct ia_css_pipe *acc_pipe;
enum sh_css_pipe_config_override copy_ovrd;
enum ia_css_input_mode preview_pipe_input_mode;
unsigned int thread_id;
@@ -3705,7 +3537,6 @@ preview_start(struct ia_css_pipe *pipe)
copy_pipe = pipe->pipe_settings.preview.copy_pipe;
capture_pipe = pipe->pipe_settings.preview.capture_pipe;
- acc_pipe = pipe->pipe_settings.preview.acc_pipe;
sh_css_metrics_start_frame();
@@ -3764,22 +3595,6 @@ preview_start(struct ia_css_pipe *pipe)
(enum mipi_port_id)0);
}
- if (acc_pipe) {
- sh_css_sp_init_pipeline(&acc_pipe->pipeline,
- IA_CSS_PIPE_ID_ACC,
- (uint8_t)ia_css_pipe_get_pipe_num(acc_pipe),
- false,
- pipe->stream->config.pixels_per_clock == 2,
- false, /* continuous */
- false, /* offline */
- pipe->required_bds_factor,
- 0,
- IA_CSS_INPUT_MODE_MEMORY,
- NULL,
- NULL,
- (enum mipi_port_id)0);
- }
-
start_pipe(pipe, copy_ovrd, preview_pipe_input_mode);
IA_CSS_LEAVE_ERR_PRIVATE(err);
@@ -3850,9 +3665,7 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe,
pipeline = &pipe->pipeline;
- assert(pipeline ||
- pipe_id == IA_CSS_PIPE_ID_COPY ||
- pipe_id == IA_CSS_PIPE_ID_ACC);
+ assert(pipeline || pipe_id == IA_CSS_PIPE_ID_COPY);
assert(sizeof(NULL) <= sizeof(ddr_buffer.kernel_ptr));
ddr_buffer.kernel_ptr = HOST_ADDRESS(NULL);
@@ -4146,7 +3959,7 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
if (!frame->valid)
pipe->num_invalid_frames--;
- if (frame->info.format == IA_CSS_FRAME_FORMAT_BINARY_8) {
+ if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_BINARY_8) {
#ifdef ISP2401
frame->planes.binary.size = frame->data_bytes;
#else
@@ -4442,16 +4255,6 @@ ia_css_dequeue_isys_event(struct ia_css_event *event)
return err;
}
-static void
-acc_start(struct ia_css_pipe *pipe)
-{
- assert(pipe);
- assert(pipe->stream);
-
- start_pipe(pipe, SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD,
- pipe->stream->config.mode);
-}
-
static int
sh_css_pipe_start(struct ia_css_stream *stream)
{
@@ -4496,9 +4299,6 @@ sh_css_pipe_start(struct ia_css_stream *stream)
case IA_CSS_PIPE_ID_YUVPP:
err = yuvpp_start(pipe);
break;
- case IA_CSS_PIPE_ID_ACC:
- acc_start(pipe);
- break;
default:
err = -EINVAL;
}
@@ -4524,10 +4324,6 @@ sh_css_pipe_start(struct ia_css_stream *stream)
stream->pipes[i]->stop_requested = false;
err = yuvpp_start(stream->pipes[i]);
break;
- case IA_CSS_PIPE_ID_ACC:
- stream->pipes[i]->stop_requested = false;
- acc_start(stream->pipes[i]);
- break;
default:
err = -EINVAL;
}
@@ -4620,22 +4416,6 @@ sh_css_pipe_start(struct ia_css_stream *stream)
(uint8_t)thread_id, 0, 0);
}
- /* in case of PREVIEW mode, check whether QOS acc_pipe is available, then start the qos pipe */
- if (pipe_id == IA_CSS_PIPE_ID_PREVIEW) {
- struct ia_css_pipe *acc_pipe = NULL;
-
- acc_pipe = pipe->pipe_settings.preview.acc_pipe;
-
- if (acc_pipe) {
- ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(acc_pipe),
- &thread_id);
- /* by the time we reach here q is initialized and handle is available.*/
- ia_css_bufq_enqueue_psys_event(
- IA_CSS_PSYS_SW_EVENT_START_STREAM,
- (uint8_t)thread_id, 0, 0);
- }
- }
-
stream->started = true;
IA_CSS_LEAVE_ERR_PRIVATE(err);
@@ -6861,8 +6641,6 @@ sh_css_pipe_load_binaries(struct ia_css_pipe *pipe)
case IA_CSS_PIPE_ID_YUVPP:
err = load_yuvpp_binaries(pipe);
break;
- case IA_CSS_PIPE_ID_ACC:
- break;
default:
err = -EINVAL;
break;
@@ -7102,7 +6880,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
/* we use output port 1 as internal output port */
tmp_in_frame = yuv_scaler_stage->args.out_frame[1];
if (pipe->pipe_settings.yuvpp.is_output_stage[i]) {
- if (tmp_vf_frame && (tmp_vf_frame->info.res.width != 0)) {
+ if (tmp_vf_frame && (tmp_vf_frame->frame_info.res.width != 0)) {
in_frame = yuv_scaler_stage->args.out_vf_frame;
err = add_vf_pp_stage(pipe, in_frame,
tmp_vf_frame,
@@ -7118,7 +6896,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
}
}
} else if (copy_stage) {
- if (vf_frame[0] && vf_frame[0]->info.res.width != 0) {
+ if (vf_frame[0] && vf_frame[0]->frame_info.res.width != 0) {
in_frame = copy_stage->args.out_vf_frame;
err = add_vf_pp_stage(pipe, in_frame, vf_frame[0],
&vf_pp_binary[0], &vf_pp_stage);
@@ -7158,10 +6936,10 @@ create_host_copy_pipeline(struct ia_css_pipe *pipe,
if (copy_on_sp(pipe) &&
pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
- ia_css_frame_info_init(&out_frame->info, JPEG_BYTES, 1,
+ ia_css_frame_info_init(&out_frame->frame_info, JPEG_BYTES, 1,
IA_CSS_FRAME_FORMAT_BINARY_8, 0);
- } else if (out_frame->info.format == IA_CSS_FRAME_FORMAT_RAW) {
- out_frame->info.raw_bit_depth =
+ } else if (out_frame->frame_info.format == IA_CSS_FRAME_FORMAT_RAW) {
+ out_frame->frame_info.raw_bit_depth =
ia_css_pipe_util_pipe_input_format_bpp(pipe);
}
@@ -7200,7 +6978,7 @@ create_host_isyscopy_capture_pipeline(struct ia_css_pipe *pipe)
ia_css_pipeline_clean(me);
/* Construct out_frame info */
- err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, 0);
+ err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, 0);
if (err)
return err;
out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
@@ -7755,154 +7533,6 @@ ia_css_stream_end_input_frame(const struct ia_css_stream *stream)
ia_css_inputfifo_end_frame(stream->config.channel_id);
}
-static void
-append_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware)
-{
- IA_CSS_ENTER_PRIVATE("l = %p, firmware = %p", l, firmware);
- if (!l) {
- IA_CSS_ERROR("NULL fw_info");
- IA_CSS_LEAVE_PRIVATE("");
- return;
- }
- while (*l)
- l = &(*l)->next;
- *l = firmware;
- /* when multiple acc extensions are loaded, 'next' can be not NULL */
- /*firmware->next = NULL;*/
- IA_CSS_LEAVE_PRIVATE("");
-}
-
-static void
-remove_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware)
-{
- assert(*l);
- assert(firmware);
- (void)l;
- (void)firmware;
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() enter:\n");
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() leave:\n");
- return; /* removing single and multiple firmware is handled in acc_unload_extension() */
-}
-
-static int upload_isp_code(struct ia_css_fw_info *firmware)
-{
- ia_css_ptr binary;
-
- if (!firmware) {
- IA_CSS_ERROR("NULL input parameter");
- return -EINVAL;
- }
- binary = firmware->info.isp.xmem_addr;
-
- if (!binary) {
- unsigned int size = firmware->blob.size;
- const unsigned char *blob;
- const unsigned char *binary_name;
-
- binary_name =
- (const unsigned char *)(IA_CSS_EXT_ISP_PROG_NAME(
- firmware));
- blob = binary_name +
- strlen((const char *)binary_name) +
- 1;
- binary = sh_css_load_blob(blob, size);
- firmware->info.isp.xmem_addr = binary;
- }
-
- if (!binary)
- return -ENOMEM;
- return 0;
-}
-
-static int
-acc_load_extension(struct ia_css_fw_info *firmware)
-{
- int err;
- struct ia_css_fw_info *hd = firmware;
-
- while (hd) {
- err = upload_isp_code(hd);
- if (err)
- return err;
- hd = hd->next;
- }
-
- if (!firmware)
- return -EINVAL;
- firmware->loaded = true;
- return 0;
-}
-
-static void
-acc_unload_extension(struct ia_css_fw_info *firmware)
-{
- struct ia_css_fw_info *hd = firmware;
- struct ia_css_fw_info *hdn = NULL;
-
- if (!firmware) /* should not happen */
- return;
- /* unload and remove multiple firmwares */
- while (hd) {
- hdn = (hd->next) ? &(*hd->next) : NULL;
- if (hd->info.isp.xmem_addr) {
- hmm_free(hd->info.isp.xmem_addr);
- hd->info.isp.xmem_addr = mmgr_NULL;
- }
- hd->isp_code = NULL;
- hd->next = NULL;
- hd = hdn;
- }
-
- firmware->loaded = false;
-}
-
-/* Load firmware for extension */
-static int
-ia_css_pipe_load_extension(struct ia_css_pipe *pipe,
- struct ia_css_fw_info *firmware)
-{
- int err = 0;
-
- IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe);
-
- if ((!firmware) || (!pipe)) {
- IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
- return -EINVAL;
- }
-
- if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT)
- append_firmware(&pipe->output_stage, firmware);
- else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER)
- append_firmware(&pipe->vf_stage, firmware);
- err = acc_load_extension(firmware);
-
- IA_CSS_LEAVE_ERR_PRIVATE(err);
- return err;
-}
-
-/* Unload firmware for extension */
-static void
-ia_css_pipe_unload_extension(struct ia_css_pipe *pipe,
- struct ia_css_fw_info *firmware)
-{
- IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe);
-
- if ((!firmware) || (!pipe)) {
- IA_CSS_ERROR("NULL input parameters");
- IA_CSS_LEAVE_PRIVATE("");
- return;
- }
-
- if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT)
- remove_firmware(&pipe->output_stage, firmware);
- else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER)
- remove_firmware(&pipe->vf_stage, firmware);
- acc_unload_extension(firmware);
-
- IA_CSS_LEAVE_PRIVATE("");
-}
-
bool
ia_css_pipeline_uses_params(struct ia_css_pipeline *me)
{
@@ -7924,35 +7554,6 @@ ia_css_pipeline_uses_params(struct ia_css_pipeline *me)
return false;
}
-static int
-sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline,
- const void *acc_fw)
-{
- struct ia_css_fw_info *fw = (struct ia_css_fw_info *)acc_fw;
- /* In QoS case, load_extension already called, so skipping */
- int err = 0;
-
- if (!fw->loaded)
- err = acc_load_extension(fw);
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "sh_css_pipeline_add_acc_stage() enter: pipeline=%p, acc_fw=%p\n",
- pipeline, acc_fw);
-
- if (!err) {
- struct ia_css_pipeline_stage_desc stage_desc;
-
- ia_css_pipe_get_acc_stage_desc(&stage_desc, NULL, fw);
- err = ia_css_pipeline_create_and_add_stage(pipeline,
- &stage_desc,
- NULL);
- }
-
- ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
- "sh_css_pipeline_add_acc_stage() leave: return_err=%d\n", err);
- return err;
-}
-
/*
* @brief Tag a specific frame in continuous capture.
* Refer to "sh_css_internal.h" for details.
@@ -8177,26 +7778,6 @@ void ia_css_stream_config_defaults(struct ia_css_stream_config *stream_config)
stream_config->source.port.rxcount = 0x04040404;
}
-static int
-ia_css_acc_pipe_create(struct ia_css_pipe *pipe)
-{
- int err = 0;
-
- if (!pipe) {
- IA_CSS_ERROR("NULL input parameter");
- return -EINVAL;
- }
-
- /* There is not meaning for num_execs = 0 semantically. Run at least once. */
- if (pipe->config.acc_num_execs == 0)
- pipe->config.acc_num_execs = 1;
-
- if (pipe->config.acc_extension)
- err = ia_css_pipe_load_extension(pipe, pipe->config.acc_extension);
-
- return err;
-}
-
int ia_css_pipe_create(const struct ia_css_pipe_config *config,
struct ia_css_pipe **pipe)
{
@@ -8257,23 +7838,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config,
else
ia_css_pipe_extra_config_defaults(&internal_pipe->extra_config);
- if (config->mode == IA_CSS_PIPE_MODE_ACC) {
- /*
- * Temporary hack to migrate acceleration to CSS 2.0.
- * In the future the code for all pipe types should be
- * unified.
- */
- *pipe = internal_pipe;
- if (!internal_pipe->config.acc_extension &&
- internal_pipe->config.num_acc_stages ==
- 0) { /* if no acc binary and no standalone stage */
- *pipe = NULL;
- IA_CSS_LEAVE_ERR_PRIVATE(0);
- return 0;
- }
- return ia_css_acc_pipe_create(internal_pipe);
- }
-
/*
* Use config value when dvs_frame_delay setting equal to 2,
* otherwise always 1 by default
@@ -8368,15 +7932,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config,
}
}
}
- if (internal_pipe->config.acc_extension) {
- err = ia_css_pipe_load_extension(internal_pipe,
- internal_pipe->config.acc_extension);
- if (err) {
- IA_CSS_LEAVE_ERR_PRIVATE(err);
- kvfree(internal_pipe);
- return err;
- }
- }
/* set all info to zeroes first */
memset(&internal_pipe->info, 0, sizeof(internal_pipe->info));
@@ -8525,57 +8080,6 @@ find_pipe(struct ia_css_pipe *pipes[], unsigned int num_pipes,
}
static int
-ia_css_acc_stream_create(struct ia_css_stream *stream)
-{
- int i;
- int err = 0;
-
- IA_CSS_ENTER_PRIVATE("stream = %p", stream);
-
- if (!stream) {
- IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
- return -EINVAL;
- }
-
- for (i = 0; i < stream->num_pipes; i++) {
- struct ia_css_pipe *pipe = stream->pipes[i];
-
- if (!pipe) {
- IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
- return -EINVAL;
- }
-
- pipe->stream = stream;
- }
-
- /* Map SP threads before doing anything. */
- err = map_sp_threads(stream, true);
- if (err) {
- IA_CSS_LEAVE_ERR_PRIVATE(err);
- return err;
- }
-
- for (i = 0; i < stream->num_pipes; i++) {
- struct ia_css_pipe *pipe = stream->pipes[i];
-
- assert(pipe);
- ia_css_pipe_map_queue(pipe, true);
- }
-
- err = create_host_pipeline_structure(stream);
- if (err) {
- IA_CSS_LEAVE_ERR_PRIVATE(err);
- return err;
- }
-
- stream->started = false;
-
- IA_CSS_LEAVE_ERR_PRIVATE(0);
-
- return 0;
-}
-
-static int
metadata_info_init(const struct ia_css_metadata_config *mdc,
struct ia_css_metadata_info *md)
{
@@ -8807,11 +8311,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
goto ERR;
IA_CSS_LOG("isp_params_configs: %p", curr_stream->isp_params_configs);
- if (num_pipes == 1 && pipes[0]->config.mode == IA_CSS_PIPE_MODE_ACC) {
- *stream = curr_stream;
- err = ia_css_acc_stream_create(curr_stream);
- goto ERR;
- }
/* sensor binning */
if (!spcopyonly) {
sensor_binning_changed =
@@ -8832,7 +8331,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
/* Search for the preview pipe and create the copy pipe */
struct ia_css_pipe *preview_pipe;
struct ia_css_pipe *video_pipe;
- struct ia_css_pipe *acc_pipe;
struct ia_css_pipe *capture_pipe = NULL;
struct ia_css_pipe *copy_pipe = NULL;
@@ -8847,11 +8345,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
IA_CSS_PIPE_MODE_PREVIEW, false);
video_pipe = find_pipe(pipes, num_pipes,
IA_CSS_PIPE_MODE_VIDEO, false);
- acc_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_ACC,
- false);
- if (acc_pipe && num_pipes == 2 && curr_stream->cont_capt)
- curr_stream->cont_capt =
- false; /* preview + QoS case will not need cont_capt switch */
+
if (curr_stream->cont_capt) {
capture_pipe = find_pipe(pipes, num_pipes,
IA_CSS_PIPE_MODE_CAPTURE,
@@ -8888,9 +8382,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
}
if (video_pipe && curr_stream->cont_capt)
video_pipe->pipe_settings.video.capture_pipe = capture_pipe;
-
- if (preview_pipe && acc_pipe)
- preview_pipe->pipe_settings.preview.acc_pipe = acc_pipe;
}
for (i = 0; i < num_pipes; i++) {
curr_pipe = pipes[i];
@@ -9738,13 +9229,6 @@ void ia_css_pipe_map_queue(struct ia_css_pipe *pipe, bool map)
if (!pipe->stream->config.continuous)
ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map);
ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map);
- } else if (pipe->mode == IA_CSS_PIPE_ID_ACC) {
- if (need_input_queue)
- ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_INPUT_FRAME, map);
- ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map);
- ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PARAMETER_SET, map);
- ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PER_FRAME_PARAMETER_SET, map);
- ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map);
} else if (pipe->mode == IA_CSS_PIPE_ID_YUVPP) {
unsigned int idx;
@@ -9795,92 +9279,6 @@ ia_css_unlock_raw_frame(struct ia_css_stream *stream, uint32_t exp_id)
return ret;
}
-/*
- * @brief Set the state (Enable or Disable) of the Extension stage in the
- * given pipe.
- */
-int
-ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle,
- bool enable)
-{
- unsigned int thread_id;
- struct ia_css_pipeline_stage *stage;
- int err = 0;
-
- IA_CSS_ENTER("");
-
- /* Parameter Check */
- if (!pipe || !pipe->stream) {
- IA_CSS_ERROR("Invalid Pipe.");
- err = -EINVAL;
- } else if (!(pipe->config.acc_extension)) {
- IA_CSS_ERROR("Invalid Pipe(No Extension Firmware)");
- err = -EINVAL;
- } else if (!sh_css_sp_is_running()) {
- IA_CSS_ERROR("Leaving: queue unavailable.");
- err = -EBUSY;
- } else {
- /* Query the threadid and stage_num for the Extension firmware*/
- ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
- err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage);
- if (!err) {
- /* Set the Extension State;. TODO: Add check for stage firmware.type (QOS)*/
- err = ia_css_bufq_enqueue_psys_event(
- (uint8_t)IA_CSS_PSYS_SW_EVENT_STAGE_ENABLE_DISABLE,
- (uint8_t)thread_id,
- (uint8_t)stage->stage_num,
- enable ? 1 : 0);
- if (!err) {
- if (enable)
- SH_CSS_QOS_STAGE_ENABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num);
- else
- SH_CSS_QOS_STAGE_DISABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num);
- }
- }
- }
- IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, enable);
- return err;
-}
-
-/*
- * @brief Get the state (Enable or Disable) of the Extension stage in the
- * given pipe.
- */
-int
-ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle,
- bool *enable)
-{
- struct ia_css_pipeline_stage *stage;
- unsigned int thread_id;
- int err = 0;
-
- IA_CSS_ENTER("");
-
- /* Parameter Check */
- if (!pipe || !pipe->stream) {
- IA_CSS_ERROR("Invalid Pipe.");
- err = -EINVAL;
- } else if (!(pipe->config.acc_extension)) {
- IA_CSS_ERROR("Invalid Pipe (No Extension Firmware).");
- err = -EINVAL;
- } else if (!sh_css_sp_is_running()) {
- IA_CSS_ERROR("Leaving: queue unavailable.");
- err = -EBUSY;
- } else {
- /* Query the threadid and stage_num corresponding to the Extension firmware*/
- ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
- err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage);
-
- if (!err) {
- /* Get the Extension State */
- *enable = (SH_CSS_QOS_STAGE_IS_ENABLED(&sh_css_sp_group.pipe[thread_id],
- stage->stage_num)) ? true : false;
- }
- }
- IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, *enable);
- return err;
-}
-
static void
sh_css_hmm_buffer_record_init(void)
{
diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h
index 435b3cedd1c3..d98f1323441e 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_internal.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h
@@ -488,16 +488,6 @@ ia_css_metadata_free_multiple(unsigned int num_bufs,
/* Macro for handling pipe_qos_config */
#define QOS_INVALID (~0U)
-#define QOS_ALL_STAGES_DISABLED (0U)
-#define QOS_STAGE_MASK(num) (0x00000001 << num)
-#define SH_CSS_IS_QOS_PIPE(pipe) ((pipe)->pipe_qos_config != QOS_INVALID)
-#define SH_CSS_QOS_STAGE_ENABLE(pipe, num) ((pipe)->pipe_qos_config |= QOS_STAGE_MASK(num))
-#define SH_CSS_QOS_STAGE_DISABLE(pipe, num) ((pipe)->pipe_qos_config &= ~QOS_STAGE_MASK(num))
-#define SH_CSS_QOS_STAGE_IS_ENABLED(pipe, num) ((pipe)->pipe_qos_config & QOS_STAGE_MASK(num))
-#define SH_CSS_QOS_STAGE_IS_ALL_DISABLED(pipe) ((pipe)->pipe_qos_config == QOS_ALL_STAGES_DISABLED)
-#define SH_CSS_QOS_MODE_PIPE_ADD(mode, pipe) ((mode) |= (0x1 << (pipe)->pipe_id))
-#define SH_CSS_QOS_MODE_PIPE_REMOVE(mode, pipe) ((mode) &= ~(0x1 << (pipe)->pipe_id))
-#define SH_CSS_IS_QOS_ONLY_MODE(mode) ((mode) == (0x1 << IA_CSS_PIPE_ID_ACC))
/* Information for a pipeline */
struct sh_css_sp_pipeline {
@@ -907,9 +897,6 @@ sh_css_params_init(void);
void
sh_css_params_uninit(void);
-/* For Acceleration API: Flush FW (shared buffer pointer) arguments */
-void sh_css_flush(struct ia_css_acc_fw *fw);
-
void
sh_css_binary_args_reset(struct sh_css_binary_args *args);
diff --git a/drivers/staging/media/atomisp/pci/sh_css_legacy.h b/drivers/staging/media/atomisp/pci/sh_css_legacy.h
index 567c8d6dcc2c..cdf239b070a8 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_legacy.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_legacy.h
@@ -32,7 +32,6 @@ enum ia_css_pipe_id {
IA_CSS_PIPE_ID_VIDEO,
IA_CSS_PIPE_ID_CAPTURE,
IA_CSS_PIPE_ID_YUVPP,
- IA_CSS_PIPE_ID_ACC,
IA_CSS_PIPE_ID_NUM
};
diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
index 41a4c9162319..5b43cc656269 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
@@ -13,6 +13,7 @@
* more details.
*/
+#include <linux/math.h>
#include <linux/slab.h>
#include <math_support.h>
@@ -239,10 +240,9 @@ prepare_shading_table(const struct ia_css_shading_table *in_table,
{
unsigned int input_width, input_height, table_width, table_height, i;
unsigned int left_padding, top_padding, left_cropping;
- unsigned int bds_numerator, bds_denominator;
- int right_padding;
-
struct ia_css_shading_table *result;
+ struct u32_fract bds;
+ int right_padding;
assert(target_table);
assert(binary);
@@ -265,17 +265,16 @@ prepare_shading_table(const struct ia_css_shading_table *in_table,
left_cropping = (binary->info->sp.pipeline.left_cropping == 0) ?
binary->dvs_envelope.width : 2 * ISP_VEC_NELEMS;
- sh_css_bds_factor_get_numerator_denominator
- (bds_factor, &bds_numerator, &bds_denominator);
+ sh_css_bds_factor_get_fract(bds_factor, &bds);
left_padding = (left_padding + binary->info->sp.pipeline.left_cropping) *
- bds_numerator / bds_denominator -
+ bds.numerator / bds.denominator -
binary->info->sp.pipeline.left_cropping;
right_padding = (binary->internal_frame_info.res.width -
- binary->effective_in_frame_res.width * bds_denominator /
- bds_numerator - left_cropping) * bds_numerator / bds_denominator;
- top_padding = binary->info->sp.pipeline.top_cropping * bds_numerator /
- bds_denominator -
+ binary->effective_in_frame_res.width * bds.denominator /
+ bds.numerator - left_cropping) * bds.numerator / bds.denominator;
+ top_padding = binary->info->sp.pipeline.top_cropping * bds.numerator /
+ bds.denominator -
binary->info->sp.pipeline.top_cropping;
/*
diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c
index 67915d76a87f..f08564f58242 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_params.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_params.c
@@ -936,8 +936,8 @@ sh_css_set_black_frame(struct ia_css_stream *stream,
assert(raw_black_frame);
params = stream->isp_params_configs;
- height = raw_black_frame->info.res.height;
- width = raw_black_frame->info.padded_width;
+ height = raw_black_frame->frame_info.res.height;
+ width = raw_black_frame->frame_info.padded_width;
ptr = raw_black_frame->data
+ raw_black_frame->planes.raw.offset;
@@ -1187,16 +1187,15 @@ ia_css_process_zoom_and_motion(
const struct sh_css_binary_args *args = &stage->args;
const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};
- if (args->out_frame[0])
- out_infos[0] = &args->out_frame[0]->info;
+ out_infos[0] = ia_css_frame_get_info(args->out_frame[0]);
+
info = &stage->firmware->info.isp;
ia_css_binary_fill_info(info, false, false,
ATOMISP_INPUT_FORMAT_RAW_10,
- args->in_frame ? &args->in_frame->info : NULL,
+ ia_css_frame_get_info(args->in_frame),
NULL,
out_infos,
- args->out_vf_frame ? &args->out_vf_frame->info
- : NULL,
+ ia_css_frame_get_info(args->out_vf_frame),
&tmp_binary,
NULL,
-1, true);
@@ -3461,10 +3460,10 @@ sh_css_params_write_to_ddr_internal(
if (stage->args.delay_frames[0]) {
/*When delay frames are present(as in case of video),
they are used for dvs. Configure DVS using those params*/
- dvs_in_frame_info = &stage->args.delay_frames[0]->info;
+ dvs_in_frame_info = &stage->args.delay_frames[0]->frame_info;
} else {
/*Otherwise, use input frame to configure DVS*/
- dvs_in_frame_info = &stage->args.in_frame->info;
+ dvs_in_frame_info = &stage->args.in_frame->frame_info;
}
/* Generate default DVS unity table on start up*/
diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c
index 615500a7d3c4..0dd58a7fe2cc 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_sp.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c
@@ -277,10 +277,10 @@ sh_css_sp_start_raw_copy(struct ia_css_frame *out_frame,
ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
pipe = &sh_css_sp_group.pipe[thread_id];
- pipe->copy.raw.height = out_frame->info.res.height;
- pipe->copy.raw.width = out_frame->info.res.width;
- pipe->copy.raw.padded_width = out_frame->info.padded_width;
- pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth;
+ pipe->copy.raw.height = out_frame->frame_info.res.height;
+ pipe->copy.raw.width = out_frame->frame_info.res.width;
+ pipe->copy.raw.padded_width = out_frame->frame_info.padded_width;
+ pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth;
pipe->copy.raw.max_input_width = max_input_width;
pipe->num_stages = 1;
pipe->pipe_id = pipe_id;
@@ -351,10 +351,10 @@ sh_css_sp_start_isys_copy(struct ia_css_frame *out_frame,
ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
pipe = &sh_css_sp_group.pipe[thread_id];
- pipe->copy.raw.height = out_frame->info.res.height;
- pipe->copy.raw.width = out_frame->info.res.width;
- pipe->copy.raw.padded_width = out_frame->info.padded_width;
- pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth;
+ pipe->copy.raw.height = out_frame->frame_info.res.height;
+ pipe->copy.raw.width = out_frame->frame_info.res.width;
+ pipe->copy.raw.padded_width = out_frame->frame_info.padded_width;
+ pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth;
pipe->copy.raw.max_input_width = max_input_width;
pipe->num_stages = 1;
pipe->pipe_id = pipe_id;
@@ -451,9 +451,9 @@ sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out,
frame_in->data,
frame_in->buf_type);
- ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->info);
+ ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->frame_info);
- switch (frame_in->info.format) {
+ switch (frame_in->frame_info.format) {
case IA_CSS_FRAME_FORMAT_RAW_PACKED:
case IA_CSS_FRAME_FORMAT_RAW:
sp_frame_out->planes.raw.offset = frame_in->planes.raw.offset;
@@ -536,7 +536,7 @@ set_input_frame_buffer(const struct ia_css_frame *frame)
if (!frame)
return -EINVAL;
- switch (frame->info.format) {
+ switch (frame->frame_info.format) {
case IA_CSS_FRAME_FORMAT_QPLANE6:
case IA_CSS_FRAME_FORMAT_YUV420_16:
case IA_CSS_FRAME_FORMAT_RAW_PACKED:
@@ -567,7 +567,7 @@ set_output_frame_buffer(const struct ia_css_frame *frame,
if (!frame)
return -EINVAL;
- switch (frame->info.format) {
+ switch (frame->frame_info.format) {
case IA_CSS_FRAME_FORMAT_YUV420:
case IA_CSS_FRAME_FORMAT_YUV422:
case IA_CSS_FRAME_FORMAT_YUV444:
@@ -608,7 +608,7 @@ set_view_finder_buffer(const struct ia_css_frame *frame)
if (!frame)
return -EINVAL;
- switch (frame->info.format) {
+ switch (frame->frame_info.format) {
/* the dual output pin */
case IA_CSS_FRAME_FORMAT_NV12:
case IA_CSS_FRAME_FORMAT_NV12_16:
@@ -819,34 +819,35 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
ret = ia_css_fpn_configure(binary, &binary->in_frame_info);
if (ret)
return ret;
- ret = ia_css_crop_configure(binary, &args->delay_frames[0]->info);
+ ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0]));
if (ret)
return ret;
ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info);
if (ret)
return ret;
- ret = ia_css_output0_configure(binary, &args->out_frame[0]->info);
+ ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
if (ret)
return ret;
- ret = ia_css_output1_configure(binary, &args->out_vf_frame->info);
+ ret = ia_css_output1_configure(binary, ia_css_frame_get_info(args->out_vf_frame));
if (ret)
return ret;
ret = ia_css_copy_output_configure(binary, args->copy_output);
if (ret)
return ret;
- ret = ia_css_output0_configure(binary, &args->out_frame[0]->info);
+ ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
if (ret)
return ret;
- ret = ia_css_iterator_configure(binary, &args->in_frame->info);
+ ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame));
if (ret)
return ret;
- ret = ia_css_dvs_configure(binary, &args->out_frame[0]->info);
+ ret = ia_css_dvs_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
if (ret)
return ret;
- ret = ia_css_output_configure(binary, &args->out_frame[0]->info);
+ ret = ia_css_output_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
if (ret)
return ret;
- ret = ia_css_raw_configure(pipeline, binary, &args->in_frame->info, &binary->in_frame_info, two_ppc, deinterleaved);
+ ret = ia_css_raw_configure(pipeline, binary, ia_css_frame_get_info(args->in_frame),
+ &binary->in_frame_info, two_ppc, deinterleaved);
if (ret)
return ret;
@@ -1037,7 +1038,7 @@ sh_css_sp_init_stage(struct ia_css_binary *binary,
return -EINVAL;
if (args->in_frame)
- ia_css_get_crop_offsets(pipe, &args->in_frame->info);
+ ia_css_get_crop_offsets(pipe, &args->in_frame->frame_info);
else
ia_css_get_crop_offsets(pipe, &binary->in_frame_info);
#else
@@ -1124,15 +1125,14 @@ sp_init_stage(struct ia_css_pipeline_stage *stage,
const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};
if (args->out_frame[0])
- out_infos[0] = &args->out_frame[0]->info;
+ out_infos[0] = &args->out_frame[0]->frame_info;
info = &firmware->info.isp;
ia_css_binary_fill_info(info, false, false,
ATOMISP_INPUT_FORMAT_RAW_10,
- args->in_frame ? &args->in_frame->info : NULL,
+ ia_css_frame_get_info(args->in_frame),
NULL,
out_infos,
- args->out_vf_frame ? &args->out_vf_frame->info
- : NULL,
+ ia_css_frame_get_info(args->out_vf_frame),
&tmp_binary,
NULL,
-1, true);
@@ -1266,7 +1266,7 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
sh_css_sp_group.pipe[thread_id].thread_id = thread_id;
sh_css_sp_group.pipe[thread_id].pipe_num = pipe_num;
sh_css_sp_group.pipe[thread_id].num_execs = me->num_execs;
- sh_css_sp_group.pipe[thread_id].pipe_qos_config = me->pipe_qos_config;
+ sh_css_sp_group.pipe[thread_id].pipe_qos_config = QOS_INVALID;
sh_css_sp_group.pipe[thread_id].required_bds_factor = required_bds_factor;
sh_css_sp_group.pipe[thread_id].input_system_mode
= (uint32_t)input_mode;
diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig
new file mode 100644
index 000000000000..418841ea5a0d
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/Kconfig
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Atmel media platform drivers"
+
+config VIDEO_ATMEL_ISC
+ tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select REGMAP_MMIO
+ select V4L2_FWNODE
+ select VIDEO_ATMEL_ISC_BASE
+ help
+ This module makes the ATMEL Image Sensor Controller available
+ as a v4l2 device.
+
+ This driver is deprecated and is scheduled for removal by
+ the beginning of 2026. See the TODO file for more information.
+
+config VIDEO_ATMEL_XISC
+ tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select REGMAP_MMIO
+ select V4L2_FWNODE
+ select VIDEO_ATMEL_ISC_BASE
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This module makes the ATMEL eXtended Image Sensor Controller
+ available as a v4l2 device.
+
+ This driver is deprecated and is scheduled for removal by
+ the beginning of 2026. See the TODO file for more information.
+
+config VIDEO_ATMEL_ISC_BASE
+ tristate
+ default n
+ help
+ ATMEL ISC and XISC common code base.
diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile
new file mode 100644
index 000000000000..34eaeeac5bba
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+atmel-isc-objs = atmel-sama5d2-isc.o
+atmel-xisc-objs = atmel-sama7g5-isc.o
+atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o
+
+obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o
+obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o
+obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o
diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO
new file mode 100644
index 000000000000..71691df07a80
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/TODO
@@ -0,0 +1,34 @@
+The Atmel ISC driver is not compliant with media controller specification.
+In order to evolve this driver, it has to move to media controller, to
+support enhanced features and future products which embed it.
+The move to media controller involves several changes which are
+not backwards compatible with the current usability of the driver.
+
+The best example is the way the format is propagated from the top video
+driver /dev/videoX down to the sensor.
+
+In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt
+and controls the sensor directly. This is achieved by having a lot of code
+inside the driver that will query the subdev at probe time and make a list
+of formats which are usable.
+Basically the user has nothing to configure, as the isc will handle
+everything at the top level. This is an easy way to capture, but also comes
+with the drawback of lack of flexibility.
+In a more complicated pipeline
+sensor ==> controller 1 ==> controller 2 ==> isc
+this will not be achievable, as controller 1 and controller 2 might be
+media-controller configurable, and will not propagate the formats down to
+the sensor.
+
+After discussions with the media maintainers, the decision is to move
+Atmel ISC to staging as-is, to keep the Kconfig symbols and the users
+to the driver in staging. Thus, all the existing users of the non
+media-controller paradigm will continue to be happy and use the old config
+way.
+
+The new driver was added in the media subsystem with a different
+symbol, with the conversion to media controller done, and new users
+of the driver will be able to use all the new features.
+
+The replacement driver is named VIDEO_MICROCHIP_ISC or
+VIDEO_MICROCHIP_XISC depending on the product flavor.
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
new file mode 100644
index 000000000000..99e61bbfc9bc
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
@@ -0,0 +1,2011 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Microchip Image Sensor Controller (ISC) common driver base
+ *
+ * Copyright (C) 2016-2019 Microchip Technology, Inc.
+ *
+ * Author: Songjun Wu
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+#include <linux/atmel-isc-media.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "atmel-isc-regs.h"
+#include "atmel-isc.h"
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static unsigned int sensor_preferred = 1;
+module_param(sensor_preferred, uint, 0644);
+MODULE_PARM_DESC(sensor_preferred,
+ "Sensor is preferred to output the specified format (1-on 0-off), default 1");
+
+#define ISC_IS_FORMAT_RAW(mbus_code) \
+ (((mbus_code) & 0xf000) == 0x3000)
+
+#define ISC_IS_FORMAT_GREY(mbus_code) \
+ (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \
+ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8)))
+
+static inline void isc_update_v4l2_ctrls(struct isc_device *isc)
+{
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ /* In here we set the v4l2 controls w.r.t. our pipeline config */
+ v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
+ v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
+ v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
+ v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
+
+ v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]);
+ v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]);
+ v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]);
+ v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]);
+}
+
+static inline void isc_update_awb_ctrls(struct isc_device *isc)
+{
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ /* In here we set our actual hw pipeline config */
+
+ regmap_write(isc->regmap, ISC_WB_O_RGR,
+ ((ctrls->offset[ISC_HIS_CFG_MODE_R])) |
+ ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
+ regmap_write(isc->regmap, ISC_WB_O_BGB,
+ ((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
+ ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
+ regmap_write(isc->regmap, ISC_WB_G_RGR,
+ ctrls->gain[ISC_HIS_CFG_MODE_R] |
+ (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
+ regmap_write(isc->regmap, ISC_WB_G_BGB,
+ ctrls->gain[ISC_HIS_CFG_MODE_B] |
+ (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16));
+}
+
+static inline void isc_reset_awb_ctrls(struct isc_device *isc)
+{
+ unsigned int c;
+
+ for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
+ /* gains have a fixed point at 9 decimals */
+ isc->ctrls.gain[c] = 1 << 9;
+ /* offsets are in 2's complements */
+ isc->ctrls.offset[c] = 0;
+ }
+}
+
+
+static int isc_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct isc_device *isc = vb2_get_drv_priv(vq);
+ unsigned int size = isc->fmt.fmt.pix.sizeimage;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int isc_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = isc->fmt.fmt.pix.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ vbuf->field = isc->fmt.fmt.pix.field;
+
+ return 0;
+}
+
+static void isc_crop_pfe(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 h, w;
+
+ h = isc->fmt.fmt.pix.height;
+ w = isc->fmt.fmt.pix.width;
+
+ /*
+ * In case the sensor is not RAW, it will output a pixel (12-16 bits)
+ * with two samples on the ISC Data bus (which is 8-12)
+ * ISC will count each sample, so, we need to multiply these values
+ * by two, to get the real number of samples for the required pixels.
+ */
+ if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) {
+ h <<= 1;
+ w <<= 1;
+ }
+
+ /*
+ * We limit the column/row count that the ISC will output according
+ * to the configured resolution that we want.
+ * This will avoid the situation where the sensor is misconfigured,
+ * sending more data, and the ISC will just take it and DMA to memory,
+ * causing corruption.
+ */
+ regmap_write(regmap, ISC_PFE_CFG1,
+ (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) |
+ (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK));
+
+ regmap_write(regmap, ISC_PFE_CFG2,
+ (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) |
+ (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK));
+
+ regmap_update_bits(regmap, ISC_PFE_CFG0,
+ ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN,
+ ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN);
+}
+
+static void isc_start_dma(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 sizeimage = isc->fmt.fmt.pix.sizeimage;
+ u32 dctrl_dview;
+ dma_addr_t addr0;
+
+ addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
+ regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0);
+
+ switch (isc->config.fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
+ addr0 + (sizeimage * 2) / 3);
+ regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
+ addr0 + (sizeimage * 5) / 6);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
+ addr0 + sizeimage / 2);
+ regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
+ addr0 + (sizeimage * 3) / 4);
+ break;
+ default:
+ break;
+ }
+
+ dctrl_dview = isc->config.dctrl_dview;
+
+ regmap_write(regmap, ISC_DCTRL + isc->offsets.dma,
+ dctrl_dview | ISC_DCTRL_IE_IS);
+ spin_lock(&isc->awb_lock);
+ regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
+ spin_unlock(&isc->awb_lock);
+}
+
+static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
+{
+ struct regmap *regmap = isc->regmap;
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ u32 val, bay_cfg;
+ const u32 *gamma;
+ unsigned int i;
+
+ /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
+ for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
+ val = pipeline & BIT(i) ? 1 : 0;
+ regmap_field_write(isc->pipeline[i], val);
+ }
+
+ if (!pipeline)
+ return;
+
+ bay_cfg = isc->config.sd_format->cfa_baycfg;
+
+ regmap_write(regmap, ISC_WB_CFG, bay_cfg);
+ isc_update_awb_ctrls(isc);
+ isc_update_v4l2_ctrls(isc);
+
+ regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
+
+ gamma = &isc->gamma_table[ctrls->gamma_index][0];
+ regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
+ regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
+ regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
+
+ isc->config_dpc(isc);
+ isc->config_csc(isc);
+ isc->config_cbc(isc);
+ isc->config_cc(isc);
+ isc->config_gam(isc);
+}
+
+static int isc_update_profile(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 sr;
+ int counter = 100;
+
+ regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+
+ regmap_read(regmap, ISC_CTRLSR, &sr);
+ while ((sr & ISC_CTRL_UPPRO) && counter--) {
+ usleep_range(1000, 2000);
+ regmap_read(regmap, ISC_CTRLSR, &sr);
+ }
+
+ if (counter < 0) {
+ v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void isc_set_histogram(struct isc_device *isc, bool enable)
+{
+ struct regmap *regmap = isc->regmap;
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ if (enable) {
+ regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
+ ISC_HIS_CFG_MODE_GR |
+ (isc->config.sd_format->cfa_baycfg
+ << ISC_HIS_CFG_BAYSEL_SHIFT) |
+ ISC_HIS_CFG_RAR);
+ regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
+ ISC_HIS_CTRL_EN);
+ regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
+ ctrls->hist_id = ISC_HIS_CFG_MODE_GR;
+ isc_update_profile(isc);
+ regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+ ctrls->hist_stat = HIST_ENABLED;
+ } else {
+ regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
+ regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
+ ISC_HIS_CTRL_DIS);
+
+ ctrls->hist_stat = HIST_DISABLED;
+ }
+}
+
+static int isc_configure(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 pfe_cfg0, dcfg, mask, pipeline;
+ struct isc_subdev_entity *subdev = isc->current_subdev;
+
+ pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
+ pipeline = isc->config.bits_pipeline;
+
+ dcfg = isc->config.dcfg_imode | isc->dcfg;
+
+ pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
+ mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
+ ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
+ ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC |
+ ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI;
+
+ regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
+
+ isc->config_rlp(isc);
+
+ regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg);
+
+ /* Set the pipeline */
+ isc_set_pipeline(isc, pipeline);
+
+ /*
+ * The current implemented histogram is available for RAW R, B, GB, GR
+ * channels. We need to check if sensor is outputting RAW BAYER
+ */
+ if (isc->ctrls.awb &&
+ ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
+ isc_set_histogram(isc, true);
+ else
+ isc_set_histogram(isc, false);
+
+ /* Update profile */
+ return isc_update_profile(isc);
+}
+
+static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct isc_device *isc = vb2_get_drv_priv(vq);
+ struct regmap *regmap = isc->regmap;
+ struct isc_buffer *buf;
+ unsigned long flags;
+ int ret;
+
+ /* Enable stream on the sub device */
+ ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n",
+ ret);
+ goto err_start_stream;
+ }
+
+ ret = pm_runtime_resume_and_get(isc->dev);
+ if (ret < 0) {
+ v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n",
+ ret);
+ goto err_pm_get;
+ }
+
+ ret = isc_configure(isc);
+ if (unlikely(ret))
+ goto err_configure;
+
+ /* Enable DMA interrupt */
+ regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE);
+
+ spin_lock_irqsave(&isc->dma_queue_lock, flags);
+
+ isc->sequence = 0;
+ isc->stop = false;
+ reinit_completion(&isc->comp);
+
+ isc->cur_frm = list_first_entry(&isc->dma_queue,
+ struct isc_buffer, list);
+ list_del(&isc->cur_frm->list);
+
+ isc_crop_pfe(isc);
+ isc_start_dma(isc);
+
+ spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
+
+ /* if we streaming from RAW, we can do one-shot white balance adj */
+ if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
+ v4l2_ctrl_activate(isc->do_wb_ctrl, true);
+
+ return 0;
+
+err_configure:
+ pm_runtime_put_sync(isc->dev);
+err_pm_get:
+ v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
+
+err_start_stream:
+ spin_lock_irqsave(&isc->dma_queue_lock, flags);
+ list_for_each_entry(buf, &isc->dma_queue, list)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ INIT_LIST_HEAD(&isc->dma_queue);
+ spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
+
+ return ret;
+}
+
+static void isc_stop_streaming(struct vb2_queue *vq)
+{
+ struct isc_device *isc = vb2_get_drv_priv(vq);
+ unsigned long flags;
+ struct isc_buffer *buf;
+ int ret;
+
+ mutex_lock(&isc->awb_mutex);
+ v4l2_ctrl_activate(isc->do_wb_ctrl, false);
+
+ isc->stop = true;
+
+ /* Wait until the end of the current frame */
+ if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
+ v4l2_err(&isc->v4l2_dev,
+ "Timeout waiting for end of the capture\n");
+
+ mutex_unlock(&isc->awb_mutex);
+
+ /* Disable DMA interrupt */
+ regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
+
+ pm_runtime_put_sync(isc->dev);
+
+ /* Disable stream on the sub device */
+ ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
+ if (ret && ret != -ENOIOCTLCMD)
+ v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n");
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&isc->dma_queue_lock, flags);
+ if (unlikely(isc->cur_frm)) {
+ vb2_buffer_done(&isc->cur_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ isc->cur_frm = NULL;
+ }
+ list_for_each_entry(buf, &isc->dma_queue, list)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ INIT_LIST_HEAD(&isc->dma_queue);
+ spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
+}
+
+static void isc_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb);
+ struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+
+ spin_lock_irqsave(&isc->dma_queue_lock, flags);
+ if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
+ vb2_start_streaming_called(vb->vb2_queue)) {
+ isc->cur_frm = buf;
+ isc_start_dma(isc);
+ } else
+ list_add_tail(&buf->list, &isc->dma_queue);
+ spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
+}
+
+static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
+ unsigned int fourcc)
+{
+ unsigned int num_formats = isc->num_user_formats;
+ struct isc_format *fmt;
+ unsigned int i;
+
+ for (i = 0; i < num_formats; i++) {
+ fmt = isc->user_formats[i];
+ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct vb2_ops isc_vb2_ops = {
+ .queue_setup = isc_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_prepare = isc_buffer_prepare,
+ .start_streaming = isc_start_streaming,
+ .stop_streaming = isc_stop_streaming,
+ .buf_queue = isc_buffer_queue,
+};
+
+static int isc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ strscpy(cap->driver, "microchip-isc", sizeof(cap->driver));
+ strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", isc->v4l2_dev.name);
+
+ return 0;
+}
+
+static int isc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct isc_device *isc = video_drvdata(file);
+ u32 index = f->index;
+ u32 i, supported_index;
+
+ if (index < isc->controller_formats_size) {
+ f->pixelformat = isc->controller_formats[index].fourcc;
+ return 0;
+ }
+
+ index -= isc->controller_formats_size;
+
+ supported_index = 0;
+
+ for (i = 0; i < isc->formats_list_size; i++) {
+ if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) ||
+ !isc->formats_list[i].sd_support)
+ continue;
+ if (supported_index == index) {
+ f->pixelformat = isc->formats_list[i].fourcc;
+ return 0;
+ }
+ supported_index++;
+ }
+
+ return -EINVAL;
+}
+
+static int isc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ *fmt = isc->fmt;
+
+ return 0;
+}
+
+/*
+ * Checks the current configured format, if ISC can output it,
+ * considering which type of format the ISC receives from the sensor
+ */
+static int isc_try_validate_formats(struct isc_device *isc)
+{
+ int ret;
+ bool bayer = false, yuv = false, rgb = false, grey = false;
+
+ /* all formats supported by the RLP module are OK */
+ switch (isc->try_config.fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ ret = 0;
+ bayer = true;
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ ret = 0;
+ yuv = true;
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ARGB444:
+ case V4L2_PIX_FMT_ARGB555:
+ ret = 0;
+ rgb = true;
+ break;
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y16:
+ ret = 0;
+ grey = true;
+ break;
+ default:
+ /* any other different formats are not supported */
+ ret = -EINVAL;
+ }
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
+ rgb, yuv, grey, bayer);
+
+ /* we cannot output RAW if we do not receive RAW */
+ if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
+ return -EINVAL;
+
+ /* we cannot output GREY if we do not receive RAW/GREY */
+ if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
+ !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code))
+ return -EINVAL;
+
+ return ret;
+}
+
+/*
+ * Configures the RLP and DMA modules, depending on the output format
+ * configured for the ISC.
+ * If direct_dump == true, just dump raw data 8/16 bits depending on format.
+ */
+static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
+{
+ isc->try_config.rlp_cfg_mode = 0;
+
+ switch (isc->try_config.fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 8;
+ isc->try_config.bpp_v4l2 = 8;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_ARGB444:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_ARGB555:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 32;
+ isc->try_config.bpp_v4l2 = 32;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
+ isc->try_config.bpp = 12;
+ isc->try_config.bpp_v4l2 = 8; /* only first plane */
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 8; /* only first plane */
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ case V4L2_PIX_FMT_GREY:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 8;
+ isc->try_config.bpp_v4l2 = 8;
+ break;
+ case V4L2_PIX_FMT_Y16:
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH;
+ fallthrough;
+ case V4L2_PIX_FMT_Y10:
+ isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (direct_dump) {
+ isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
+ isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+ isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Configuring pipeline modules, depending on which format the ISC outputs
+ * and considering which format it has as input from the sensor.
+ */
+static int isc_try_configure_pipeline(struct isc_device *isc)
+{
+ switch (isc->try_config.fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_ARGB444:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ /* if sensor format is RAW, we convert inside ISC */
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
+ isc->try_config.bits_pipeline = CFA_ENABLE |
+ WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE |
+ CC_ENABLE;
+ } else {
+ isc->try_config.bits_pipeline = 0x0;
+ }
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ /* if sensor format is RAW, we convert inside ISC */
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
+ isc->try_config.bits_pipeline = CFA_ENABLE |
+ CSC_ENABLE | GAM_ENABLES | WB_ENABLE |
+ SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE |
+ DPC_BLCENABLE;
+ } else {
+ isc->try_config.bits_pipeline = 0x0;
+ }
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ /* if sensor format is RAW, we convert inside ISC */
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
+ isc->try_config.bits_pipeline = CFA_ENABLE |
+ CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
+ SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
+ } else {
+ isc->try_config.bits_pipeline = 0x0;
+ }
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ /* if sensor format is RAW, we convert inside ISC */
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
+ isc->try_config.bits_pipeline = CFA_ENABLE |
+ CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
+ SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
+ } else {
+ isc->try_config.bits_pipeline = 0x0;
+ }
+ break;
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_Y16:
+ /* if sensor format is RAW, we convert inside ISC */
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
+ isc->try_config.bits_pipeline = CFA_ENABLE |
+ CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
+ CBC_ENABLE | DPC_BLCENABLE;
+ } else {
+ isc->try_config.bits_pipeline = 0x0;
+ }
+ break;
+ default:
+ if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
+ isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE;
+ else
+ isc->try_config.bits_pipeline = 0x0;
+ }
+
+ /* Tune the pipeline to product specific */
+ isc->adapt_pipeline(isc);
+
+ return 0;
+}
+
+static void isc_try_fse(struct isc_device *isc,
+ struct v4l2_subdev_state *sd_state)
+{
+ int ret;
+ struct v4l2_subdev_frame_size_enum fse = {};
+
+ /*
+ * If we do not know yet which format the subdev is using, we cannot
+ * do anything.
+ */
+ if (!isc->try_config.sd_format)
+ return;
+
+ fse.code = isc->try_config.sd_format->mbus_code;
+ fse.which = V4L2_SUBDEV_FORMAT_TRY;
+
+ ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
+ sd_state, &fse);
+ /*
+ * Attempt to obtain format size from subdev. If not available,
+ * just use the maximum ISC can receive.
+ */
+ if (ret) {
+ sd_state->pads->try_crop.width = isc->max_width;
+ sd_state->pads->try_crop.height = isc->max_height;
+ } else {
+ sd_state->pads->try_crop.width = fse.max_width;
+ sd_state->pads->try_crop.height = fse.max_height;
+ }
+}
+
+static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
+ u32 *code)
+{
+ int i;
+ struct isc_format *sd_fmt = NULL, *direct_fmt = NULL;
+ struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg = {};
+ struct v4l2_subdev_state pad_state = {
+ .pads = &pad_cfg
+ };
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ u32 mbus_code;
+ int ret;
+ bool rlp_dma_direct_dump = false;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* Step 1: find a RAW format that is supported */
+ for (i = 0; i < isc->num_user_formats; i++) {
+ if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) {
+ sd_fmt = isc->user_formats[i];
+ break;
+ }
+ }
+ /* Step 2: We can continue with this RAW format, or we can look
+ * for better: maybe sensor supports directly what we need.
+ */
+ direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat);
+
+ /* Step 3: We have both. We decide given the module parameter which
+ * one to use.
+ */
+ if (direct_fmt && sd_fmt && sensor_preferred)
+ sd_fmt = direct_fmt;
+
+ /* Step 4: we do not have RAW but we have a direct format. Use it. */
+ if (direct_fmt && !sd_fmt)
+ sd_fmt = direct_fmt;
+
+ /* Step 5: if we are using a direct format, we need to package
+ * everything as 8 bit data and just dump it
+ */
+ if (sd_fmt == direct_fmt)
+ rlp_dma_direct_dump = true;
+
+ /* Step 6: We have no format. This can happen if the userspace
+ * requests some weird/invalid format.
+ * In this case, default to whatever we have
+ */
+ if (!sd_fmt && !direct_fmt) {
+ sd_fmt = isc->user_formats[isc->num_user_formats - 1];
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "Sensor not supporting %.4s, using %.4s\n",
+ (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc);
+ }
+
+ if (!sd_fmt) {
+ ret = -EINVAL;
+ goto isc_try_fmt_err;
+ }
+
+ /* Step 7: Print out what we decided for debugging */
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "Preferring to have sensor using format %.4s\n",
+ (char *)&sd_fmt->fourcc);
+
+ /* Step 8: at this moment we decided which format the subdev will use */
+ isc->try_config.sd_format = sd_fmt;
+
+ /* Limit to Atmel ISC hardware capabilities */
+ if (pixfmt->width > isc->max_width)
+ pixfmt->width = isc->max_width;
+ if (pixfmt->height > isc->max_height)
+ pixfmt->height = isc->max_height;
+
+ /*
+ * The mbus format is the one the subdev outputs.
+ * The pixels will be transferred in this format Sensor -> ISC
+ */
+ mbus_code = sd_fmt->mbus_code;
+
+ /*
+ * Validate formats. If the required format is not OK, default to raw.
+ */
+
+ isc->try_config.fourcc = pixfmt->pixelformat;
+
+ if (isc_try_validate_formats(isc)) {
+ pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc;
+ /* Re-try to validate the new format */
+ ret = isc_try_validate_formats(isc);
+ if (ret)
+ goto isc_try_fmt_err;
+ }
+
+ ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump);
+ if (ret)
+ goto isc_try_fmt_err;
+
+ ret = isc_try_configure_pipeline(isc);
+ if (ret)
+ goto isc_try_fmt_err;
+
+ /* Obtain frame sizes if possible to have crop requirements ready */
+ isc_try_fse(isc, &pad_state);
+
+ v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
+ ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
+ &pad_state, &format);
+ if (ret < 0)
+ goto isc_try_fmt_subdev_err;
+
+ v4l2_fill_pix_format(pixfmt, &format.format);
+
+ /* Limit to Atmel ISC hardware capabilities */
+ if (pixfmt->width > isc->max_width)
+ pixfmt->width = isc->max_width;
+ if (pixfmt->height > isc->max_height)
+ pixfmt->height = isc->max_height;
+
+ pixfmt->field = V4L2_FIELD_NONE;
+ pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3;
+ pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) *
+ pixfmt->height;
+
+ if (code)
+ *code = mbus_code;
+
+ return 0;
+
+isc_try_fmt_err:
+ v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n");
+isc_try_fmt_subdev_err:
+ memset(&isc->try_config, 0, sizeof(isc->try_config));
+
+ return ret;
+}
+
+static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
+{
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ u32 mbus_code = 0;
+ int ret;
+
+ ret = isc_try_fmt(isc, f, &mbus_code);
+ if (ret)
+ return ret;
+
+ v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
+ ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
+ set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ /* Limit to Atmel ISC hardware capabilities */
+ if (f->fmt.pix.width > isc->max_width)
+ f->fmt.pix.width = isc->max_width;
+ if (f->fmt.pix.height > isc->max_height)
+ f->fmt.pix.height = isc->max_height;
+
+ isc->fmt = *f;
+
+ if (isc->try_config.sd_format && isc->config.sd_format &&
+ isc->try_config.sd_format != isc->config.sd_format) {
+ isc->ctrls.hist_stat = HIST_INIT;
+ isc_reset_awb_ctrls(isc);
+ isc_update_v4l2_ctrls(isc);
+ }
+ /* make the try configuration active */
+ isc->config = isc->try_config;
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n");
+
+ return 0;
+}
+
+static int isc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ if (vb2_is_busy(&isc->vb2_vidq))
+ return -EBUSY;
+
+ return isc_set_fmt(isc, f);
+}
+
+static int isc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ return isc_try_fmt(isc, f, NULL);
+}
+
+static int isc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = 0;
+ strscpy(inp->name, "Camera", sizeof(inp->name));
+
+ return 0;
+}
+
+static int isc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int isc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
+}
+
+static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isc_device *isc = video_drvdata(file);
+
+ return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
+}
+
+static int isc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct isc_device *isc = video_drvdata(file);
+ int ret = -EINVAL;
+ int i;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ for (i = 0; i < isc->num_user_formats; i++)
+ if (isc->user_formats[i]->fourcc == fsize->pixel_format)
+ ret = 0;
+
+ for (i = 0; i < isc->controller_formats_size; i++)
+ if (isc->controller_formats[i].fourcc == fsize->pixel_format)
+ ret = 0;
+
+ if (ret)
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+
+ fsize->stepwise.min_width = 16;
+ fsize->stepwise.max_width = isc->max_width;
+ fsize->stepwise.min_height = 16;
+ fsize->stepwise.max_height = isc->max_height;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isc_ioctl_ops = {
+ .vidioc_querycap = isc_querycap,
+ .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap,
+
+ .vidioc_enum_input = isc_enum_input,
+ .vidioc_g_input = isc_g_input,
+ .vidioc_s_input = isc_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_g_parm = isc_g_parm,
+ .vidioc_s_parm = isc_s_parm,
+ .vidioc_enum_framesizes = isc_enum_framesizes,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int isc_open(struct file *file)
+{
+ struct isc_device *isc = video_drvdata(file);
+ struct v4l2_subdev *sd = isc->current_subdev->sd;
+ int ret;
+
+ if (mutex_lock_interruptible(&isc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ if (!v4l2_fh_is_singular_file(file))
+ goto unlock;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ v4l2_fh_release(file);
+ goto unlock;
+ }
+
+ ret = isc_set_fmt(isc, &isc->fmt);
+ if (ret) {
+ v4l2_subdev_call(sd, core, s_power, 0);
+ v4l2_fh_release(file);
+ }
+
+unlock:
+ mutex_unlock(&isc->lock);
+ return ret;
+}
+
+static int isc_release(struct file *file)
+{
+ struct isc_device *isc = video_drvdata(file);
+ struct v4l2_subdev *sd = isc->current_subdev->sd;
+ bool fh_singular;
+ int ret;
+
+ mutex_lock(&isc->lock);
+
+ fh_singular = v4l2_fh_is_singular_file(file);
+
+ ret = _vb2_fop_release(file, NULL);
+
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+ mutex_unlock(&isc->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations isc_fops = {
+ .owner = THIS_MODULE,
+ .open = isc_open,
+ .release = isc_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+irqreturn_t atmel_isc_interrupt(int irq, void *dev_id)
+{
+ struct isc_device *isc = (struct isc_device *)dev_id;
+ struct regmap *regmap = isc->regmap;
+ u32 isc_intsr, isc_intmask, pending;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(regmap, ISC_INTSR, &isc_intsr);
+ regmap_read(regmap, ISC_INTMASK, &isc_intmask);
+
+ pending = isc_intsr & isc_intmask;
+
+ if (likely(pending & ISC_INT_DDONE)) {
+ spin_lock(&isc->dma_queue_lock);
+ if (isc->cur_frm) {
+ struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
+ struct vb2_buffer *vb = &vbuf->vb2_buf;
+
+ vb->timestamp = ktime_get_ns();
+ vbuf->sequence = isc->sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ isc->cur_frm = NULL;
+ }
+
+ if (!list_empty(&isc->dma_queue) && !isc->stop) {
+ isc->cur_frm = list_first_entry(&isc->dma_queue,
+ struct isc_buffer, list);
+ list_del(&isc->cur_frm->list);
+
+ isc_start_dma(isc);
+ }
+
+ if (isc->stop)
+ complete(&isc->comp);
+
+ ret = IRQ_HANDLED;
+ spin_unlock(&isc->dma_queue_lock);
+ }
+
+ if (pending & ISC_INT_HISDONE) {
+ schedule_work(&isc->awb_work);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_isc_interrupt);
+
+static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
+{
+ struct regmap *regmap = isc->regmap;
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
+ u32 *hist_entry = &ctrls->hist_entry[0];
+ u32 i;
+
+ *min = 0;
+ *max = HIST_ENTRIES;
+
+ regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry,
+ hist_entry, HIST_ENTRIES);
+
+ *hist_count = 0;
+ /*
+ * we deliberately ignore the end of the histogram,
+ * the most white pixels
+ */
+ for (i = 1; i < HIST_ENTRIES; i++) {
+ if (*hist_entry && !*min)
+ *min = i;
+ if (*hist_entry)
+ *max = i;
+ *hist_count += i * (*hist_entry++);
+ }
+
+ if (!*min)
+ *min = 1;
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: hist_id %u, hist_count %u",
+ ctrls->hist_id, *hist_count);
+}
+
+static void isc_wb_update(struct isc_ctrls *ctrls)
+{
+ struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls);
+ u32 *hist_count = &ctrls->hist_count[0];
+ u32 c, offset[4];
+ u64 avg = 0;
+ /* We compute two gains, stretch gain and grey world gain */
+ u32 s_gain[4], gw_gain[4];
+
+ /*
+ * According to Grey World, we need to set gains for R/B to normalize
+ * them towards the green channel.
+ * Thus we want to keep Green as fixed and adjust only Red/Blue
+ * Compute the average of the both green channels first
+ */
+ avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] +
+ (u64)hist_count[ISC_HIS_CFG_MODE_GB];
+ avg >>= 1;
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: green components average %llu\n", avg);
+
+ /* Green histogram is null, nothing to do */
+ if (!avg)
+ return;
+
+ for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
+ /*
+ * the color offset is the minimum value of the histogram.
+ * we stretch this color to the full range by substracting
+ * this value from the color component.
+ */
+ offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX];
+ /*
+ * The offset is always at least 1. If the offset is 1, we do
+ * not need to adjust it, so our result must be zero.
+ * the offset is computed in a histogram on 9 bits (0..512)
+ * but the offset in register is based on
+ * 12 bits pipeline (0..4096).
+ * we need to shift with the 3 bits that the histogram is
+ * ignoring
+ */
+ ctrls->offset[c] = (offset[c] - 1) << 3;
+
+ /*
+ * the offset is then taken and converted to 2's complements,
+ * and must be negative, as we subtract this value from the
+ * color components
+ */
+ ctrls->offset[c] = -ctrls->offset[c];
+
+ /*
+ * the stretch gain is the total number of histogram bins
+ * divided by the actual range of color component (Max - Min)
+ * If we compute gain like this, the actual color component
+ * will be stretched to the full histogram.
+ * We need to shift 9 bits for precision, we have 9 bits for
+ * decimals
+ */
+ s_gain[c] = (HIST_ENTRIES << 9) /
+ (ctrls->hist_minmax[c][HIST_MAX_INDEX] -
+ ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1);
+
+ /*
+ * Now we have to compute the gain w.r.t. the average.
+ * Add/lose gain to the component towards the average.
+ * If it happens that the component is zero, use the
+ * fixed point value : 1.0 gain.
+ */
+ if (hist_count[c])
+ gw_gain[c] = div_u64(avg << 9, hist_count[c]);
+ else
+ gw_gain[c] = 1 << 9;
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: component %d, s_gain %u, gw_gain %u\n",
+ c, s_gain[c], gw_gain[c]);
+ /* multiply both gains and adjust for decimals */
+ ctrls->gain[c] = s_gain[c] * gw_gain[c];
+ ctrls->gain[c] >>= 9;
+
+ /* make sure we are not out of range */
+ ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: component %d, final gain %u\n",
+ c, ctrls->gain[c]);
+ }
+}
+
+static void isc_awb_work(struct work_struct *w)
+{
+ struct isc_device *isc =
+ container_of(w, struct isc_device, awb_work);
+ struct regmap *regmap = isc->regmap;
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ u32 hist_id = ctrls->hist_id;
+ u32 baysel;
+ unsigned long flags;
+ u32 min, max;
+ int ret;
+
+ if (ctrls->hist_stat != HIST_ENABLED)
+ return;
+
+ isc_hist_count(isc, &min, &max);
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
+
+ ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
+ ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
+
+ if (hist_id != ISC_HIS_CFG_MODE_B) {
+ hist_id++;
+ } else {
+ isc_wb_update(ctrls);
+ hist_id = ISC_HIS_CFG_MODE_GR;
+ }
+
+ ctrls->hist_id = hist_id;
+ baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT;
+
+ ret = pm_runtime_resume_and_get(isc->dev);
+ if (ret < 0)
+ return;
+
+ /*
+ * only update if we have all the required histograms and controls
+ * if awb has been disabled, we need to reset registers as well.
+ */
+ if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) {
+ /*
+ * It may happen that DMA Done IRQ will trigger while we are
+ * updating white balance registers here.
+ * In that case, only parts of the controls have been updated.
+ * We can avoid that by locking the section.
+ */
+ spin_lock_irqsave(&isc->awb_lock, flags);
+ isc_update_awb_ctrls(isc);
+ spin_unlock_irqrestore(&isc->awb_lock, flags);
+
+ /*
+ * if we are doing just the one time white balance adjustment,
+ * we are basically done.
+ */
+ if (ctrls->awb == ISC_WB_ONETIME) {
+ v4l2_info(&isc->v4l2_dev,
+ "Completed one time white-balance adjustment.\n");
+ /* update the v4l2 controls values */
+ isc_update_v4l2_ctrls(isc);
+ ctrls->awb = ISC_WB_NONE;
+ }
+ }
+ regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
+ hist_id | baysel | ISC_HIS_CFG_RAR);
+
+ /*
+ * We have to make sure the streaming has not stopped meanwhile.
+ * ISC requires a frame to clock the internal profile update.
+ * To avoid issues, lock the sequence with a mutex
+ */
+ mutex_lock(&isc->awb_mutex);
+
+ /* streaming is not active anymore */
+ if (isc->stop) {
+ mutex_unlock(&isc->awb_mutex);
+ return;
+ }
+
+ isc_update_profile(isc);
+
+ mutex_unlock(&isc->awb_mutex);
+
+ /* if awb has been disabled, we don't need to start another histogram */
+ if (ctrls->awb)
+ regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+ pm_runtime_put_sync(isc->dev);
+}
+
+static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isc_device *isc = container_of(ctrl->handler,
+ struct isc_device, ctrls.handler);
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
+ break;
+ case V4L2_CID_GAMMA:
+ ctrls->gamma_index = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_ctrl_ops = {
+ .s_ctrl = isc_s_ctrl,
+};
+
+static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isc_device *isc = container_of(ctrl->handler,
+ struct isc_device, ctrls.handler);
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ if (ctrl->val == 1)
+ ctrls->awb = ISC_WB_AUTO;
+ else
+ ctrls->awb = ISC_WB_NONE;
+
+ /* configure the controls with new values from v4l2 */
+ if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
+ ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
+
+ if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val;
+ if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
+ ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
+
+ isc_update_awb_ctrls(isc);
+
+ mutex_lock(&isc->awb_mutex);
+ if (vb2_is_streaming(&isc->vb2_vidq)) {
+ /*
+ * If we are streaming, we can update profile to
+ * have the new settings in place.
+ */
+ isc_update_profile(isc);
+ } else {
+ /*
+ * The auto cluster will activate automatically this
+ * control. This has to be deactivated when not
+ * streaming.
+ */
+ v4l2_ctrl_activate(isc->do_wb_ctrl, false);
+ }
+ mutex_unlock(&isc->awb_mutex);
+
+ /* if we have autowhitebalance on, start histogram procedure */
+ if (ctrls->awb == ISC_WB_AUTO &&
+ vb2_is_streaming(&isc->vb2_vidq) &&
+ ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
+ isc_set_histogram(isc, true);
+
+ /*
+ * for one time whitebalance adjustment, check the button,
+ * if it's pressed, perform the one time operation.
+ */
+ if (ctrls->awb == ISC_WB_NONE &&
+ ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
+ !(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
+ V4L2_CTRL_FLAG_INACTIVE)) {
+ ctrls->awb = ISC_WB_ONETIME;
+ isc_set_histogram(isc, true);
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "One time white-balance started.\n");
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isc_device *isc = container_of(ctrl->handler,
+ struct isc_device, ctrls.handler);
+ struct isc_ctrls *ctrls = &isc->ctrls;
+
+ switch (ctrl->id) {
+ /* being a cluster, this id will be called for every control */
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->cluster[ISC_CTRL_R_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_R];
+ ctrl->cluster[ISC_CTRL_B_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_B];
+ ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_GR];
+ ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
+ ctrls->gain[ISC_HIS_CFG_MODE_GB];
+
+ ctrl->cluster[ISC_CTRL_R_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_R];
+ ctrl->cluster[ISC_CTRL_B_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_B];
+ ctrl->cluster[ISC_CTRL_GR_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_GR];
+ ctrl->cluster[ISC_CTRL_GB_OFF]->val =
+ ctrls->offset[ISC_HIS_CFG_MODE_GB];
+ break;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_awb_ops = {
+ .s_ctrl = isc_s_awb_ctrl,
+ .g_volatile_ctrl = isc_g_volatile_awb_ctrl,
+};
+
+#define ISC_CTRL_OFF(_name, _id, _name_str) \
+ static const struct v4l2_ctrl_config _name = { \
+ .ops = &isc_awb_ops, \
+ .id = _id, \
+ .name = _name_str, \
+ .type = V4L2_CTRL_TYPE_INTEGER, \
+ .flags = V4L2_CTRL_FLAG_SLIDER, \
+ .min = -4095, \
+ .max = 4095, \
+ .step = 1, \
+ .def = 0, \
+ }
+
+ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset");
+ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset");
+ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset");
+ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset");
+
+#define ISC_CTRL_GAIN(_name, _id, _name_str) \
+ static const struct v4l2_ctrl_config _name = { \
+ .ops = &isc_awb_ops, \
+ .id = _id, \
+ .name = _name_str, \
+ .type = V4L2_CTRL_TYPE_INTEGER, \
+ .flags = V4L2_CTRL_FLAG_SLIDER, \
+ .min = 0, \
+ .max = 8191, \
+ .step = 1, \
+ .def = 512, \
+ }
+
+ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain");
+ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain");
+ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain");
+ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain");
+
+static int isc_ctrl_init(struct isc_device *isc)
+{
+ const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ int ret;
+
+ ctrls->hist_stat = HIST_INIT;
+ isc_reset_awb_ctrls(isc);
+
+ ret = v4l2_ctrl_handler_init(hdl, 13);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize product specific controls. For example, contrast */
+ isc->config_ctrls(isc, ops);
+
+ ctrls->brightness = 0;
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1,
+ isc->gamma_max);
+ isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+
+ /* do_white_balance is a button, so min,max,step,default are ignored */
+ isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
+ V4L2_CID_DO_WHITE_BALANCE,
+ 0, 0, 0, 0);
+
+ if (!isc->do_wb_ctrl) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ v4l2_ctrl_activate(isc->do_wb_ctrl, false);
+
+ isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
+ isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
+ isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL);
+ isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL);
+ isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL);
+ isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL);
+ isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL);
+ isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL);
+
+ /*
+ * The cluster is in auto mode with autowhitebalance enabled
+ * and manual mode otherwise.
+ */
+ v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true);
+
+ v4l2_ctrl_handler_setup(hdl);
+
+ return 0;
+}
+
+static int isc_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct isc_device *isc = container_of(notifier->v4l2_dev,
+ struct isc_device, v4l2_dev);
+ struct isc_subdev_entity *subdev_entity =
+ container_of(notifier, struct isc_subdev_entity, notifier);
+
+ if (video_is_registered(&isc->video_dev)) {
+ v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n");
+ return -EBUSY;
+ }
+
+ subdev_entity->sd = subdev;
+
+ return 0;
+}
+
+static void isc_async_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct isc_device *isc = container_of(notifier->v4l2_dev,
+ struct isc_device, v4l2_dev);
+ mutex_destroy(&isc->awb_mutex);
+ cancel_work_sync(&isc->awb_work);
+ video_unregister_device(&isc->video_dev);
+ v4l2_ctrl_handler_free(&isc->ctrls.handler);
+}
+
+static struct isc_format *find_format_by_code(struct isc_device *isc,
+ unsigned int code, int *index)
+{
+ struct isc_format *fmt = &isc->formats_list[0];
+ unsigned int i;
+
+ for (i = 0; i < isc->formats_list_size; i++) {
+ if (fmt->mbus_code == code) {
+ *index = i;
+ return fmt;
+ }
+
+ fmt++;
+ }
+
+ return NULL;
+}
+
+static int isc_formats_init(struct isc_device *isc)
+{
+ struct isc_format *fmt;
+ struct v4l2_subdev *subdev = isc->current_subdev->sd;
+ unsigned int num_fmts, i, j;
+ u32 list_size = isc->formats_list_size;
+ struct v4l2_subdev_mbus_code_enum mbus_code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ num_fmts = 0;
+ while (!v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code)) {
+ mbus_code.index++;
+
+ fmt = find_format_by_code(isc, mbus_code.code, &i);
+ if (!fmt) {
+ v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n",
+ mbus_code.code);
+ continue;
+ }
+
+ fmt->sd_support = true;
+ num_fmts++;
+ }
+
+ if (!num_fmts)
+ return -ENXIO;
+
+ isc->num_user_formats = num_fmts;
+ isc->user_formats = devm_kcalloc(isc->dev,
+ num_fmts, sizeof(*isc->user_formats),
+ GFP_KERNEL);
+ if (!isc->user_formats)
+ return -ENOMEM;
+
+ fmt = &isc->formats_list[0];
+ for (i = 0, j = 0; i < list_size; i++) {
+ if (fmt->sd_support)
+ isc->user_formats[j++] = fmt;
+ fmt++;
+ }
+
+ return 0;
+}
+
+static int isc_set_default_fmt(struct isc_device *isc)
+{
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = isc->user_formats[0]->fourcc,
+ },
+ };
+ int ret;
+
+ ret = isc_try_fmt(isc, &f, NULL);
+ if (ret)
+ return ret;
+
+ isc->fmt = f;
+ return 0;
+}
+
+static int isc_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct isc_device *isc = container_of(notifier->v4l2_dev,
+ struct isc_device, v4l2_dev);
+ struct video_device *vdev = &isc->video_dev;
+ struct vb2_queue *q = &isc->vb2_vidq;
+ int ret = 0;
+
+ INIT_WORK(&isc->awb_work, isc_awb_work);
+
+ ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
+ return ret;
+ }
+
+ isc->current_subdev = container_of(notifier,
+ struct isc_subdev_entity, notifier);
+ mutex_init(&isc->lock);
+ mutex_init(&isc->awb_mutex);
+
+ init_completion(&isc->comp);
+
+ /* Initialize videobuf2 queue */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = isc;
+ q->buf_struct_size = sizeof(struct isc_buffer);
+ q->ops = &isc_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &isc->lock;
+ q->min_buffers_needed = 1;
+ q->dev = isc->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ v4l2_err(&isc->v4l2_dev,
+ "vb2_queue_init() failed: %d\n", ret);
+ goto isc_async_complete_err;
+ }
+
+ /* Init video dma queues */
+ INIT_LIST_HEAD(&isc->dma_queue);
+ spin_lock_init(&isc->dma_queue_lock);
+ spin_lock_init(&isc->awb_lock);
+
+ ret = isc_formats_init(isc);
+ if (ret < 0) {
+ v4l2_err(&isc->v4l2_dev,
+ "Init format failed: %d\n", ret);
+ goto isc_async_complete_err;
+ }
+
+ ret = isc_set_default_fmt(isc);
+ if (ret) {
+ v4l2_err(&isc->v4l2_dev, "Could not set default format\n");
+ goto isc_async_complete_err;
+ }
+
+ ret = isc_ctrl_init(isc);
+ if (ret) {
+ v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
+ goto isc_async_complete_err;
+ }
+
+ /* Register video device */
+ strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &isc_fops;
+ vdev->ioctl_ops = &isc_ioctl_ops;
+ vdev->v4l2_dev = &isc->v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &isc->lock;
+ vdev->ctrl_handler = &isc->ctrls.handler;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+ video_set_drvdata(vdev, isc);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ v4l2_err(&isc->v4l2_dev,
+ "video_register_device failed: %d\n", ret);
+ goto isc_async_complete_err;
+ }
+
+ return 0;
+
+isc_async_complete_err:
+ mutex_destroy(&isc->awb_mutex);
+ mutex_destroy(&isc->lock);
+ return ret;
+}
+
+const struct v4l2_async_notifier_operations atmel_isc_async_ops = {
+ .bound = isc_async_bound,
+ .unbind = isc_async_unbind,
+ .complete = isc_async_complete,
+};
+EXPORT_SYMBOL_GPL(atmel_isc_async_ops);
+
+void atmel_isc_subdev_cleanup(struct isc_device *isc)
+{
+ struct isc_subdev_entity *subdev_entity;
+
+ list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
+ v4l2_async_nf_unregister(&subdev_entity->notifier);
+ v4l2_async_nf_cleanup(&subdev_entity->notifier);
+ }
+
+ INIT_LIST_HEAD(&isc->subdev_entities);
+}
+EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup);
+
+int atmel_isc_pipeline_init(struct isc_device *isc)
+{
+ struct device *dev = isc->dev;
+ struct regmap *regmap = isc->regmap;
+ struct regmap_field *regs;
+ unsigned int i;
+
+ /*
+ * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC-->
+ * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420
+ */
+ const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = {
+ REG_FIELD(ISC_DPC_CTRL, 0, 0),
+ REG_FIELD(ISC_DPC_CTRL, 1, 1),
+ REG_FIELD(ISC_DPC_CTRL, 2, 2),
+ REG_FIELD(ISC_WB_CTRL, 0, 0),
+ REG_FIELD(ISC_CFA_CTRL, 0, 0),
+ REG_FIELD(ISC_CC_CTRL, 0, 0),
+ REG_FIELD(ISC_GAM_CTRL, 0, 0),
+ REG_FIELD(ISC_GAM_CTRL, 1, 1),
+ REG_FIELD(ISC_GAM_CTRL, 2, 2),
+ REG_FIELD(ISC_GAM_CTRL, 3, 3),
+ REG_FIELD(ISC_VHXS_CTRL, 0, 0),
+ REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0),
+ REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0),
+ REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0),
+ REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0),
+ };
+
+ for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
+ regs = devm_regmap_field_alloc(dev, regmap, regfields[i]);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ isc->pipeline[i] = regs;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init);
+
+/* regmap configuration */
+#define ATMEL_ISC_REG_MAX 0xd5c
+const struct regmap_config atmel_isc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_ISC_REG_MAX,
+};
+EXPORT_SYMBOL_GPL(atmel_isc_regmap_config);
+
+MODULE_AUTHOR("Songjun Wu");
+MODULE_AUTHOR("Eugen Hristev");
+MODULE_DESCRIPTION("Atmel ISC common code base");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c
new file mode 100644
index 000000000000..d442b5f4c931
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Microchip Image Sensor Controller (ISC) common clock driver setup
+ *
+ * Copyright (C) 2016 Microchip Technology, Inc.
+ *
+ * Author: Songjun Wu
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include "atmel-isc-regs.h"
+#include "atmel-isc.h"
+
+static int isc_wait_clk_stable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ struct regmap *regmap = isc_clk->regmap;
+ unsigned long timeout = jiffies + usecs_to_jiffies(1000);
+ unsigned int status;
+
+ while (time_before(jiffies, timeout)) {
+ regmap_read(regmap, ISC_CLKSR, &status);
+ if (!(status & ISC_CLKSR_SIP))
+ return 0;
+
+ usleep_range(10, 250);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int isc_clk_prepare(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(isc_clk->dev);
+ if (ret < 0)
+ return ret;
+
+ return isc_wait_clk_stable(hw);
+}
+
+static void isc_clk_unprepare(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ isc_wait_clk_stable(hw);
+
+ pm_runtime_put_sync(isc_clk->dev);
+}
+
+static int isc_clk_enable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 id = isc_clk->id;
+ struct regmap *regmap = isc_clk->regmap;
+ unsigned long flags;
+ unsigned int status;
+
+ dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
+ __func__, id, isc_clk->div, isc_clk->parent_id);
+
+ spin_lock_irqsave(&isc_clk->lock, flags);
+ regmap_update_bits(regmap, ISC_CLKCFG,
+ ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
+ (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
+ (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
+
+ regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
+ spin_unlock_irqrestore(&isc_clk->lock, flags);
+
+ regmap_read(regmap, ISC_CLKSR, &status);
+ if (status & ISC_CLK(id))
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static void isc_clk_disable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 id = isc_clk->id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isc_clk->lock, flags);
+ regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
+ spin_unlock_irqrestore(&isc_clk->lock, flags);
+}
+
+static int isc_clk_is_enabled(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 status;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(isc_clk->dev);
+ if (ret < 0)
+ return 0;
+
+ regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
+
+ pm_runtime_put_sync(isc_clk->dev);
+
+ return status & ISC_CLK(isc_clk->id) ? 1 : 0;
+}
+
+static unsigned long
+isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
+}
+
+static int isc_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ long best_rate = -EINVAL;
+ int best_diff = -1;
+ unsigned int i, div;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent;
+ unsigned long parent_rate;
+
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_hw_get_rate(parent);
+ if (!parent_rate)
+ continue;
+
+ for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
+ unsigned long rate;
+ int diff;
+
+ rate = DIV_ROUND_CLOSEST(parent_rate, div);
+ diff = abs(req->rate - rate);
+
+ if (best_diff < 0 || best_diff > diff) {
+ best_rate = rate;
+ best_diff = diff;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+
+ if (!best_diff || rate < req->rate)
+ break;
+ }
+
+ if (!best_diff)
+ break;
+ }
+
+ dev_dbg(isc_clk->dev,
+ "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+ __func__, best_rate,
+ __clk_get_name((req->best_parent_hw)->clk),
+ req->best_parent_rate);
+
+ if (best_rate < 0)
+ return best_rate;
+
+ req->rate = best_rate;
+
+ return 0;
+}
+
+static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ isc_clk->parent_id = index;
+
+ return 0;
+}
+
+static u8 isc_clk_get_parent(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ return isc_clk->parent_id;
+}
+
+static int isc_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 div;
+
+ if (!rate)
+ return -EINVAL;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div > (ISC_CLK_MAX_DIV + 1) || !div)
+ return -EINVAL;
+
+ isc_clk->div = div - 1;
+
+ return 0;
+}
+
+static const struct clk_ops isc_clk_ops = {
+ .prepare = isc_clk_prepare,
+ .unprepare = isc_clk_unprepare,
+ .enable = isc_clk_enable,
+ .disable = isc_clk_disable,
+ .is_enabled = isc_clk_is_enabled,
+ .recalc_rate = isc_clk_recalc_rate,
+ .determine_rate = isc_clk_determine_rate,
+ .set_parent = isc_clk_set_parent,
+ .get_parent = isc_clk_get_parent,
+ .set_rate = isc_clk_set_rate,
+};
+
+static int isc_clk_register(struct isc_device *isc, unsigned int id)
+{
+ struct regmap *regmap = isc->regmap;
+ struct device_node *np = isc->dev->of_node;
+ struct isc_clk *isc_clk;
+ struct clk_init_data init;
+ const char *clk_name = np->name;
+ const char *parent_names[3];
+ int num_parents;
+
+ if (id == ISC_ISPCK && !isc->ispck_required)
+ return 0;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents < 1 || num_parents > 3)
+ return -EINVAL;
+
+ if (num_parents > 2 && id == ISC_ISPCK)
+ num_parents = 2;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ if (id == ISC_MCK)
+ of_property_read_string(np, "clock-output-names", &clk_name);
+ else
+ clk_name = "isc-ispck";
+
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.name = clk_name;
+ init.ops = &isc_clk_ops;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ isc_clk = &isc->isc_clks[id];
+ isc_clk->hw.init = &init;
+ isc_clk->regmap = regmap;
+ isc_clk->id = id;
+ isc_clk->dev = isc->dev;
+ spin_lock_init(&isc_clk->lock);
+
+ isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
+ if (IS_ERR(isc_clk->clk)) {
+ dev_err(isc->dev, "%s: clock register fail\n", clk_name);
+ return PTR_ERR(isc_clk->clk);
+ } else if (id == ISC_MCK) {
+ of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
+ }
+
+ return 0;
+}
+
+int atmel_isc_clk_init(struct isc_device *isc)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
+ isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
+ ret = isc_clk_register(isc, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_isc_clk_init);
+
+void atmel_isc_clk_cleanup(struct isc_device *isc)
+{
+ unsigned int i;
+
+ of_clk_del_provider(isc->dev->of_node);
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
+ struct isc_clk *isc_clk = &isc->isc_clks[i];
+
+ if (!IS_ERR(isc_clk->clk))
+ clk_unregister(isc_clk->clk);
+ }
+}
+EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup);
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h
new file mode 100644
index 000000000000..d06b72228d4f
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h
@@ -0,0 +1,413 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ATMEL_ISC_REGS_H
+#define __ATMEL_ISC_REGS_H
+
+#include <linux/bitops.h>
+
+/* ISC Control Enable Register 0 */
+#define ISC_CTRLEN 0x00000000
+
+/* ISC Control Disable Register 0 */
+#define ISC_CTRLDIS 0x00000004
+
+/* ISC Control Status Register 0 */
+#define ISC_CTRLSR 0x00000008
+
+#define ISC_CTRL_CAPTURE BIT(0)
+#define ISC_CTRL_UPPRO BIT(1)
+#define ISC_CTRL_HISREQ BIT(2)
+#define ISC_CTRL_HISCLR BIT(3)
+
+/* ISC Parallel Front End Configuration 0 Register */
+#define ISC_PFE_CFG0 0x0000000c
+
+#define ISC_PFE_CFG0_HPOL_LOW BIT(0)
+#define ISC_PFE_CFG0_VPOL_LOW BIT(1)
+#define ISC_PFE_CFG0_PPOL_LOW BIT(2)
+#define ISC_PFE_CFG0_CCIR656 BIT(9)
+#define ISC_PFE_CFG0_CCIR_CRC BIT(10)
+#define ISC_PFE_CFG0_MIPI BIT(14)
+
+#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4)
+#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4)
+
+#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28)
+#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28)
+#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28)
+#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28)
+#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28)
+#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28)
+
+#define ISC_PFE_CFG0_COLEN BIT(12)
+#define ISC_PFE_CFG0_ROWEN BIT(13)
+
+/* ISC Parallel Front End Configuration 1 Register */
+#define ISC_PFE_CFG1 0x00000010
+
+#define ISC_PFE_CFG1_COLMIN(v) ((v))
+#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0)
+#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16)
+#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16)
+
+/* ISC Parallel Front End Configuration 2 Register */
+#define ISC_PFE_CFG2 0x00000014
+
+#define ISC_PFE_CFG2_ROWMIN(v) ((v))
+#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0)
+#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16)
+#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16)
+
+/* ISC Clock Enable Register */
+#define ISC_CLKEN 0x00000018
+
+/* ISC Clock Disable Register */
+#define ISC_CLKDIS 0x0000001c
+
+/* ISC Clock Status Register */
+#define ISC_CLKSR 0x00000020
+#define ISC_CLKSR_SIP BIT(31)
+
+#define ISC_CLK(n) BIT(n)
+
+/* ISC Clock Configuration Register */
+#define ISC_CLKCFG 0x00000024
+#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16)
+#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16)
+#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8)
+#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8))
+
+/* ISC Interrupt Enable Register */
+#define ISC_INTEN 0x00000028
+
+/* ISC Interrupt Disable Register */
+#define ISC_INTDIS 0x0000002c
+
+/* ISC Interrupt Mask Register */
+#define ISC_INTMASK 0x00000030
+
+/* ISC Interrupt Status Register */
+#define ISC_INTSR 0x00000034
+
+#define ISC_INT_DDONE BIT(8)
+#define ISC_INT_HISDONE BIT(12)
+
+/* ISC DPC Control Register */
+#define ISC_DPC_CTRL 0x40
+
+#define ISC_DPC_CTRL_DPCEN BIT(0)
+#define ISC_DPC_CTRL_GDCEN BIT(1)
+#define ISC_DPC_CTRL_BLCEN BIT(2)
+
+/* ISC DPC Config Register */
+#define ISC_DPC_CFG 0x44
+
+#define ISC_DPC_CFG_BAYSEL_SHIFT 0
+
+#define ISC_DPC_CFG_EITPOL BIT(4)
+
+#define ISC_DPC_CFG_TA_ENABLE BIT(14)
+#define ISC_DPC_CFG_TC_ENABLE BIT(13)
+#define ISC_DPC_CFG_TM_ENABLE BIT(12)
+
+#define ISC_DPC_CFG_RE_MODE BIT(17)
+
+#define ISC_DPC_CFG_GDCCLP_SHIFT 20
+#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20)
+
+#define ISC_DPC_CFG_BLOFF_SHIFT 24
+#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24)
+
+#define ISC_DPC_CFG_BAYCFG_SHIFT 0
+#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0)
+/* ISC DPC Threshold Median Register */
+#define ISC_DPC_THRESHM 0x48
+
+/* ISC DPC Threshold Closest Register */
+#define ISC_DPC_THRESHC 0x4C
+
+/* ISC DPC Threshold Average Register */
+#define ISC_DPC_THRESHA 0x50
+
+/* ISC DPC STatus Register */
+#define ISC_DPC_SR 0x54
+
+/* ISC White Balance Control Register */
+#define ISC_WB_CTRL 0x00000058
+
+/* ISC White Balance Configuration Register */
+#define ISC_WB_CFG 0x0000005c
+
+/* ISC White Balance Offset for R, GR Register */
+#define ISC_WB_O_RGR 0x00000060
+
+/* ISC White Balance Offset for B, GB Register */
+#define ISC_WB_O_BGB 0x00000064
+
+/* ISC White Balance Gain for R, GR Register */
+#define ISC_WB_G_RGR 0x00000068
+
+/* ISC White Balance Gain for B, GB Register */
+#define ISC_WB_G_BGB 0x0000006c
+
+/* ISC Color Filter Array Control Register */
+#define ISC_CFA_CTRL 0x00000070
+
+/* ISC Color Filter Array Configuration Register */
+#define ISC_CFA_CFG 0x00000074
+#define ISC_CFA_CFG_EITPOL BIT(4)
+
+#define ISC_BAY_CFG_GRGR 0x0
+#define ISC_BAY_CFG_RGRG 0x1
+#define ISC_BAY_CFG_GBGB 0x2
+#define ISC_BAY_CFG_BGBG 0x3
+
+/* ISC Color Correction Control Register */
+#define ISC_CC_CTRL 0x00000078
+
+/* ISC Color Correction RR RG Register */
+#define ISC_CC_RR_RG 0x0000007c
+
+/* ISC Color Correction RB OR Register */
+#define ISC_CC_RB_OR 0x00000080
+
+/* ISC Color Correction GR GG Register */
+#define ISC_CC_GR_GG 0x00000084
+
+/* ISC Color Correction GB OG Register */
+#define ISC_CC_GB_OG 0x00000088
+
+/* ISC Color Correction BR BG Register */
+#define ISC_CC_BR_BG 0x0000008c
+
+/* ISC Color Correction BB OB Register */
+#define ISC_CC_BB_OB 0x00000090
+
+/* ISC Gamma Correction Control Register */
+#define ISC_GAM_CTRL 0x00000094
+
+#define ISC_GAM_CTRL_BIPART BIT(4)
+
+/* ISC_Gamma Correction Blue Entry Register */
+#define ISC_GAM_BENTRY 0x00000098
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_GENTRY 0x00000198
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_RENTRY 0x00000298
+
+/* ISC VHXS Control Register */
+#define ISC_VHXS_CTRL 0x398
+
+/* ISC VHXS Source Size Register */
+#define ISC_VHXS_SS 0x39C
+
+/* ISC VHXS Destination Size Register */
+#define ISC_VHXS_DS 0x3A0
+
+/* ISC Vertical Factor Register */
+#define ISC_VXS_FACT 0x3a4
+
+/* ISC Horizontal Factor Register */
+#define ISC_HXS_FACT 0x3a8
+
+/* ISC Vertical Config Register */
+#define ISC_VXS_CFG 0x3ac
+
+/* ISC Horizontal Config Register */
+#define ISC_HXS_CFG 0x3b0
+
+/* ISC Vertical Tap Register */
+#define ISC_VXS_TAP 0x3b4
+
+/* ISC Horizontal Tap Register */
+#define ISC_HXS_TAP 0x434
+
+/* Offset for CSC register specific to sama5d2 product */
+#define ISC_SAMA5D2_CSC_OFFSET 0
+/* Offset for CSC register specific to sama7g5 product */
+#define ISC_SAMA7G5_CSC_OFFSET 0x11c
+
+/* Color Space Conversion Control Register */
+#define ISC_CSC_CTRL 0x00000398
+
+/* Color Space Conversion YR YG Register */
+#define ISC_CSC_YR_YG 0x0000039c
+
+/* Color Space Conversion YB OY Register */
+#define ISC_CSC_YB_OY 0x000003a0
+
+/* Color Space Conversion CBR CBG Register */
+#define ISC_CSC_CBR_CBG 0x000003a4
+
+/* Color Space Conversion CBB OCB Register */
+#define ISC_CSC_CBB_OCB 0x000003a8
+
+/* Color Space Conversion CRR CRG Register */
+#define ISC_CSC_CRR_CRG 0x000003ac
+
+/* Color Space Conversion CRB OCR Register */
+#define ISC_CSC_CRB_OCR 0x000003b0
+
+/* Offset for CBC register specific to sama5d2 product */
+#define ISC_SAMA5D2_CBC_OFFSET 0
+/* Offset for CBC register specific to sama7g5 product */
+#define ISC_SAMA7G5_CBC_OFFSET 0x11c
+
+/* Contrast And Brightness Control Register */
+#define ISC_CBC_CTRL 0x000003b4
+
+/* Contrast And Brightness Configuration Register */
+#define ISC_CBC_CFG 0x000003b8
+
+/* Brightness Register */
+#define ISC_CBC_BRIGHT 0x000003bc
+#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0)
+
+/* Contrast Register */
+#define ISC_CBC_CONTRAST 0x000003c0
+#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0)
+
+/* Hue Register */
+#define ISC_CBCHS_HUE 0x4e0
+/* Saturation Register */
+#define ISC_CBCHS_SAT 0x4e4
+
+/* Offset for SUB422 register specific to sama5d2 product */
+#define ISC_SAMA5D2_SUB422_OFFSET 0
+/* Offset for SUB422 register specific to sama7g5 product */
+#define ISC_SAMA7G5_SUB422_OFFSET 0x124
+
+/* Subsampling 4:4:4 to 4:2:2 Control Register */
+#define ISC_SUB422_CTRL 0x000003c4
+
+/* Offset for SUB420 register specific to sama5d2 product */
+#define ISC_SAMA5D2_SUB420_OFFSET 0
+/* Offset for SUB420 register specific to sama7g5 product */
+#define ISC_SAMA7G5_SUB420_OFFSET 0x124
+/* Subsampling 4:2:2 to 4:2:0 Control Register */
+#define ISC_SUB420_CTRL 0x000003cc
+
+/* Offset for RLP register specific to sama5d2 product */
+#define ISC_SAMA5D2_RLP_OFFSET 0
+/* Offset for RLP register specific to sama7g5 product */
+#define ISC_SAMA7G5_RLP_OFFSET 0x124
+/* Rounding, Limiting and Packing Configuration Register */
+#define ISC_RLP_CFG 0x000003d0
+
+#define ISC_RLP_CFG_MODE_DAT8 0x0
+#define ISC_RLP_CFG_MODE_DAT9 0x1
+#define ISC_RLP_CFG_MODE_DAT10 0x2
+#define ISC_RLP_CFG_MODE_DAT11 0x3
+#define ISC_RLP_CFG_MODE_DAT12 0x4
+#define ISC_RLP_CFG_MODE_DATY8 0x5
+#define ISC_RLP_CFG_MODE_DATY10 0x6
+#define ISC_RLP_CFG_MODE_ARGB444 0x7
+#define ISC_RLP_CFG_MODE_ARGB555 0x8
+#define ISC_RLP_CFG_MODE_RGB565 0x9
+#define ISC_RLP_CFG_MODE_ARGB32 0xa
+#define ISC_RLP_CFG_MODE_YYCC 0xb
+#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc
+#define ISC_RLP_CFG_MODE_YCYC 0xd
+#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0)
+
+#define ISC_RLP_CFG_LSH BIT(5)
+
+#define ISC_RLP_CFG_YMODE_YUYV (3 << 6)
+#define ISC_RLP_CFG_YMODE_YVYU (2 << 6)
+#define ISC_RLP_CFG_YMODE_VYUY (0 << 6)
+#define ISC_RLP_CFG_YMODE_UYVY (1 << 6)
+
+#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6)
+
+/* Offset for HIS register specific to sama5d2 product */
+#define ISC_SAMA5D2_HIS_OFFSET 0
+/* Offset for HIS register specific to sama7g5 product */
+#define ISC_SAMA7G5_HIS_OFFSET 0x124
+/* Histogram Control Register */
+#define ISC_HIS_CTRL 0x000003d4
+
+#define ISC_HIS_CTRL_EN BIT(0)
+#define ISC_HIS_CTRL_DIS 0x0
+
+/* Histogram Configuration Register */
+#define ISC_HIS_CFG 0x000003d8
+
+#define ISC_HIS_CFG_MODE_GR 0x0
+#define ISC_HIS_CFG_MODE_R 0x1
+#define ISC_HIS_CFG_MODE_GB 0x2
+#define ISC_HIS_CFG_MODE_B 0x3
+#define ISC_HIS_CFG_MODE_Y 0x4
+#define ISC_HIS_CFG_MODE_RAW 0x5
+#define ISC_HIS_CFG_MODE_YCCIR656 0x6
+
+#define ISC_HIS_CFG_BAYSEL_SHIFT 4
+
+#define ISC_HIS_CFG_RAR BIT(8)
+
+/* Offset for DMA register specific to sama5d2 product */
+#define ISC_SAMA5D2_DMA_OFFSET 0
+/* Offset for DMA register specific to sama7g5 product */
+#define ISC_SAMA7G5_DMA_OFFSET 0x13c
+
+/* DMA Configuration Register */
+#define ISC_DCFG 0x000003e0
+#define ISC_DCFG_IMODE_PACKED8 0x0
+#define ISC_DCFG_IMODE_PACKED16 0x1
+#define ISC_DCFG_IMODE_PACKED32 0x2
+#define ISC_DCFG_IMODE_YC422SP 0x3
+#define ISC_DCFG_IMODE_YC422P 0x4
+#define ISC_DCFG_IMODE_YC420SP 0x5
+#define ISC_DCFG_IMODE_YC420P 0x6
+#define ISC_DCFG_IMODE_MASK GENMASK(2, 0)
+
+#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4)
+#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4)
+#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4)
+#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4)
+#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4)
+#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4)
+
+#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8)
+#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8)
+#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8)
+#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8)
+#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8)
+#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8)
+
+/* DMA Control Register */
+#define ISC_DCTRL 0x000003e4
+
+#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1)
+#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1)
+#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1)
+#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1)
+
+#define ISC_DCTRL_IE_IS (0x0 << 4)
+
+/* DMA Descriptor Address Register */
+#define ISC_DNDA 0x000003e8
+
+/* DMA Address 0 Register */
+#define ISC_DAD0 0x000003ec
+
+/* DMA Address 1 Register */
+#define ISC_DAD1 0x000003f4
+
+/* DMA Address 2 Register */
+#define ISC_DAD2 0x000003fc
+
+/* Offset for version register specific to sama5d2 product */
+#define ISC_SAMA5D2_VERSION_OFFSET 0
+#define ISC_SAMA7G5_VERSION_OFFSET 0x13c
+/* Version Register */
+#define ISC_VERSION 0x0000040c
+
+/* Offset for version register specific to sama5d2 product */
+#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0
+/* Offset for version register specific to sama7g5 product */
+#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c
+/* Histogram Entry */
+#define ISC_HIS_ENTRY 0x00000410
+
+#endif
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h
new file mode 100644
index 000000000000..dfc030b5a08f
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-isc.h
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Microchip Image Sensor Controller (ISC) driver header file
+ *
+ * Copyright (C) 2016-2019 Microchip Technology, Inc.
+ *
+ * Author: Songjun Wu
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ */
+#ifndef _ATMEL_ISC_H_
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define ISC_CLK_MAX_DIV 255
+
+enum isc_clk_id {
+ ISC_ISPCK = 0,
+ ISC_MCK = 1,
+};
+
+struct isc_clk {
+ struct clk_hw hw;
+ struct clk *clk;
+ struct regmap *regmap;
+ spinlock_t lock; /* serialize access to clock registers */
+ u8 id;
+ u8 parent_id;
+ u32 div;
+ struct device *dev;
+};
+
+#define to_isc_clk(v) container_of(v, struct isc_clk, hw)
+
+struct isc_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct isc_subdev_entity {
+ struct v4l2_subdev *sd;
+ struct v4l2_async_subdev *asd;
+ struct device_node *epn;
+ struct v4l2_async_notifier notifier;
+
+ u32 pfe_cfg0;
+
+ struct list_head list;
+};
+
+/*
+ * struct isc_format - ISC media bus format information
+ This structure represents the interface between the ISC
+ and the sensor. It's the input format received by
+ the ISC.
+ * @fourcc: Fourcc code for this format
+ * @mbus_code: V4L2 media bus format code.
+ * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer.
+ this is either BGBG, RGRG, etc.
+ * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC
+ */
+
+struct isc_format {
+ u32 fourcc;
+ u32 mbus_code;
+ u32 cfa_baycfg;
+
+ bool sd_support;
+ u32 pfe_cfg0_bps;
+};
+
+/* Pipeline bitmap */
+#define DPC_DPCENABLE BIT(0)
+#define DPC_GDCENABLE BIT(1)
+#define DPC_BLCENABLE BIT(2)
+#define WB_ENABLE BIT(3)
+#define CFA_ENABLE BIT(4)
+#define CC_ENABLE BIT(5)
+#define GAM_ENABLE BIT(6)
+#define GAM_BENABLE BIT(7)
+#define GAM_GENABLE BIT(8)
+#define GAM_RENABLE BIT(9)
+#define VHXS_ENABLE BIT(10)
+#define CSC_ENABLE BIT(11)
+#define CBC_ENABLE BIT(12)
+#define SUB422_ENABLE BIT(13)
+#define SUB420_ENABLE BIT(14)
+
+#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE)
+
+/*
+ * struct fmt_config - ISC format configuration and internal pipeline
+ This structure represents the internal configuration
+ of the ISC.
+ It also holds the format that ISC will present to v4l2.
+ * @sd_format: Pointer to an isc_format struct that holds the sensor
+ configuration.
+ * @fourcc: Fourcc code for this format.
+ * @bpp: Bytes per pixel in the current format.
+ * @bpp_v4l2: Bytes per pixel in the current format, for v4l2.
+ This differs from 'bpp' in the sense that in planar
+ formats, it refers only to the first plane.
+ * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging)
+ * @dcfg_imode: Configuration of the input of the DMA module
+ * @dctrl_dview: Configuration of the output of the DMA module
+ * @bits_pipeline: Configuration of the pipeline, which modules are enabled
+ */
+struct fmt_config {
+ struct isc_format *sd_format;
+
+ u32 fourcc;
+ u8 bpp;
+ u8 bpp_v4l2;
+
+ u32 rlp_cfg_mode;
+ u32 dcfg_imode;
+ u32 dctrl_dview;
+
+ u32 bits_pipeline;
+};
+
+#define HIST_ENTRIES 512
+#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1)
+
+enum{
+ HIST_INIT = 0,
+ HIST_ENABLED,
+ HIST_DISABLED,
+};
+
+struct isc_ctrls {
+ struct v4l2_ctrl_handler handler;
+
+ u32 brightness;
+ u32 contrast;
+ u8 gamma_index;
+#define ISC_WB_NONE 0
+#define ISC_WB_AUTO 1
+#define ISC_WB_ONETIME 2
+ u8 awb;
+
+ /* one for each component : GR, R, GB, B */
+ u32 gain[HIST_BAYER];
+ s32 offset[HIST_BAYER];
+
+ u32 hist_entry[HIST_ENTRIES];
+ u32 hist_count[HIST_BAYER];
+ u8 hist_id;
+ u8 hist_stat;
+#define HIST_MIN_INDEX 0
+#define HIST_MAX_INDEX 1
+ u32 hist_minmax[HIST_BAYER][2];
+};
+
+#define ISC_PIPE_LINE_NODE_NUM 15
+
+/*
+ * struct isc_reg_offsets - ISC device register offsets
+ * @csc: Offset for the CSC register
+ * @cbc: Offset for the CBC register
+ * @sub422: Offset for the SUB422 register
+ * @sub420: Offset for the SUB420 register
+ * @rlp: Offset for the RLP register
+ * @his: Offset for the HIS related registers
+ * @dma: Offset for the DMA related registers
+ * @version: Offset for the version register
+ * @his_entry: Offset for the HIS entries registers
+ */
+struct isc_reg_offsets {
+ u32 csc;
+ u32 cbc;
+ u32 sub422;
+ u32 sub420;
+ u32 rlp;
+ u32 his;
+ u32 dma;
+ u32 version;
+ u32 his_entry;
+};
+
+/*
+ * struct isc_device - ISC device driver data/config struct
+ * @regmap: Register map
+ * @hclock: Hclock clock input (refer datasheet)
+ * @ispck: iscpck clock (refer datasheet)
+ * @isc_clks: ISC clocks
+ * @ispck_required: ISC requires ISP Clock initialization
+ * @dcfg: DMA master configuration, architecture dependent
+ *
+ * @dev: Registered device driver
+ * @v4l2_dev: v4l2 registered device
+ * @video_dev: registered video device
+ *
+ * @vb2_vidq: video buffer 2 video queue
+ * @dma_queue_lock: lock to serialize the dma buffer queue
+ * @dma_queue: the queue for dma buffers
+ * @cur_frm: current isc frame/buffer
+ * @sequence: current frame number
+ * @stop: true if isc is not streaming, false if streaming
+ * @comp: completion reference that signals frame completion
+ *
+ * @fmt: current v42l format
+ * @user_formats: list of formats that are supported and agreed with sd
+ * @num_user_formats: how many formats are in user_formats
+ *
+ * @config: current ISC format configuration
+ * @try_config: the current ISC try format , not yet activated
+ *
+ * @ctrls: holds information about ISC controls
+ * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button
+ * @awb_work: workqueue reference for autowhitebalance histogram
+ * analysis
+ *
+ * @lock: lock for serializing userspace file operations
+ * with ISC operations
+ * @awb_mutex: serialize access to streaming status from awb work queue
+ * @awb_lock: lock for serializing awb work queue operations
+ * with DMA/buffer operations
+ *
+ * @pipeline: configuration of the ISC pipeline
+ *
+ * @current_subdev: current subdevice: the sensor
+ * @subdev_entities: list of subdevice entitites
+ *
+ * @gamma_table: pointer to the table with gamma values, has
+ * gamma_max sets of GAMMA_ENTRIES entries each
+ * @gamma_max: maximum number of sets of inside the gamma_table
+ *
+ * @max_width: maximum frame width, dependent on the internal RAM
+ * @max_height: maximum frame height, dependent on the internal RAM
+ *
+ * @config_dpc: pointer to a function that initializes product
+ * specific DPC module
+ * @config_csc: pointer to a function that initializes product
+ * specific CSC module
+ * @config_cbc: pointer to a function that initializes product
+ * specific CBC module
+ * @config_cc: pointer to a function that initializes product
+ * specific CC module
+ * @config_gam: pointer to a function that initializes product
+ * specific GAMMA module
+ * @config_rlp: pointer to a function that initializes product
+ * specific RLP module
+ * @config_ctrls: pointer to a functoin that initializes product
+ * specific v4l2 controls.
+ *
+ * @adapt_pipeline: pointer to a function that adapts the pipeline bits
+ * to the product specific pipeline
+ *
+ * @offsets: struct holding the product specific register offsets
+ * @controller_formats: pointer to the array of possible formats that the
+ * controller can output
+ * @formats_list: pointer to the array of possible formats that can
+ * be used as an input to the controller
+ * @controller_formats_size: size of controller_formats array
+ * @formats_list_size: size of formats_list array
+ */
+struct isc_device {
+ struct regmap *regmap;
+ struct clk *hclock;
+ struct clk *ispck;
+ struct isc_clk isc_clks[2];
+ bool ispck_required;
+ u32 dcfg;
+
+ struct device *dev;
+ struct v4l2_device v4l2_dev;
+ struct video_device video_dev;
+
+ struct vb2_queue vb2_vidq;
+ spinlock_t dma_queue_lock;
+ struct list_head dma_queue;
+ struct isc_buffer *cur_frm;
+ unsigned int sequence;
+ bool stop;
+ struct completion comp;
+
+ struct v4l2_format fmt;
+ struct isc_format **user_formats;
+ unsigned int num_user_formats;
+
+ struct fmt_config config;
+ struct fmt_config try_config;
+
+ struct isc_ctrls ctrls;
+ struct work_struct awb_work;
+
+ struct mutex lock;
+ struct mutex awb_mutex;
+ spinlock_t awb_lock;
+
+ struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM];
+
+ struct isc_subdev_entity *current_subdev;
+ struct list_head subdev_entities;
+
+ struct {
+#define ISC_CTRL_DO_WB 1
+#define ISC_CTRL_R_GAIN 2
+#define ISC_CTRL_B_GAIN 3
+#define ISC_CTRL_GR_GAIN 4
+#define ISC_CTRL_GB_GAIN 5
+#define ISC_CTRL_R_OFF 6
+#define ISC_CTRL_B_OFF 7
+#define ISC_CTRL_GR_OFF 8
+#define ISC_CTRL_GB_OFF 9
+ struct v4l2_ctrl *awb_ctrl;
+ struct v4l2_ctrl *do_wb_ctrl;
+ struct v4l2_ctrl *r_gain_ctrl;
+ struct v4l2_ctrl *b_gain_ctrl;
+ struct v4l2_ctrl *gr_gain_ctrl;
+ struct v4l2_ctrl *gb_gain_ctrl;
+ struct v4l2_ctrl *r_off_ctrl;
+ struct v4l2_ctrl *b_off_ctrl;
+ struct v4l2_ctrl *gr_off_ctrl;
+ struct v4l2_ctrl *gb_off_ctrl;
+ };
+
+#define GAMMA_ENTRIES 64
+ /* pointer to the defined gamma table */
+ const u32 (*gamma_table)[GAMMA_ENTRIES];
+ u32 gamma_max;
+
+ u32 max_width;
+ u32 max_height;
+
+ struct {
+ void (*config_dpc)(struct isc_device *isc);
+ void (*config_csc)(struct isc_device *isc);
+ void (*config_cbc)(struct isc_device *isc);
+ void (*config_cc)(struct isc_device *isc);
+ void (*config_gam)(struct isc_device *isc);
+ void (*config_rlp)(struct isc_device *isc);
+
+ void (*config_ctrls)(struct isc_device *isc,
+ const struct v4l2_ctrl_ops *ops);
+
+ void (*adapt_pipeline)(struct isc_device *isc);
+ };
+
+ struct isc_reg_offsets offsets;
+ const struct isc_format *controller_formats;
+ struct isc_format *formats_list;
+ u32 controller_formats_size;
+ u32 formats_list_size;
+};
+
+extern const struct regmap_config atmel_isc_regmap_config;
+extern const struct v4l2_async_notifier_operations atmel_isc_async_ops;
+
+irqreturn_t atmel_isc_interrupt(int irq, void *dev_id);
+int atmel_isc_pipeline_init(struct isc_device *isc);
+int atmel_isc_clk_init(struct isc_device *isc);
+void atmel_isc_subdev_cleanup(struct isc_device *isc);
+void atmel_isc_clk_cleanup(struct isc_device *isc);
+
+#endif
diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c
new file mode 100644
index 000000000000..ba0614f981a2
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip Image Sensor Controller (ISC) driver
+ *
+ * Copyright (C) 2016-2019 Microchip Technology, Inc.
+ *
+ * Author: Songjun Wu
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ *
+ * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
+ *
+ * ISC video pipeline integrates the following submodules:
+ * PFE: Parallel Front End to sample the camera sensor input stream
+ * WB: Programmable white balance in the Bayer domain
+ * CFA: Color filter array interpolation module
+ * CC: Programmable color correction
+ * GAM: Gamma correction
+ * CSC: Programmable color space conversion
+ * CBC: Contrast and Brightness control
+ * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
+ * RLP: This module performs rounding, range limiting
+ * and packing of the incoming data
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "atmel-isc-regs.h"
+#include "atmel-isc.h"
+
+#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592
+#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944
+
+#define ISC_SAMA5D2_PIPELINE \
+ (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
+ CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
+
+/* This is a list of the formats that the ISC can *output* */
+static const struct isc_format sama5d2_controller_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ }, {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ },
+};
+
+/* This is a list of formats that the ISC can receive as *input* */
+static struct isc_format sama5d2_formats_list[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ },
+
+};
+
+static void isc_sama5d2_config_csc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ /* Convert RGB to YUV */
+ regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
+ 0x42 | (0x81 << 16));
+ regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
+ 0x19 | (0x10 << 16));
+ regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
+ 0xFDA | (0xFB6 << 16));
+ regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
+ 0x70 | (0x80 << 16));
+ regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
+ 0x70 | (0xFA2 << 16));
+ regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
+ 0xFEE | (0x80 << 16));
+}
+
+static void isc_sama5d2_config_cbc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,
+ isc->ctrls.brightness);
+ regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,
+ isc->ctrls.contrast);
+}
+
+static void isc_sama5d2_config_cc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ /* Configure each register at the neutral fixed point 1.0 or 0.0 */
+ regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
+ regmap_write(regmap, ISC_CC_RB_OR, 0);
+ regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
+ regmap_write(regmap, ISC_CC_GB_OG, 0);
+ regmap_write(regmap, ISC_CC_BR_BG, 0);
+ regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
+}
+
+static void isc_sama5d2_config_ctrls(struct isc_device *isc,
+ const struct v4l2_ctrl_ops *ops)
+{
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ ctrls->contrast = 256;
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);
+}
+
+static void isc_sama5d2_config_dpc(struct isc_device *isc)
+{
+ /* This module is not present on sama5d2 pipeline */
+}
+
+static void isc_sama5d2_config_gam(struct isc_device *isc)
+{
+ /* No specific gamma configuration */
+}
+
+static void isc_sama5d2_config_rlp(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 rlp_mode = isc->config.rlp_cfg_mode;
+
+ /*
+ * In sama5d2, the YUV planar modes and the YUYV modes are treated
+ * in the same way in RLP register.
+ * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n)
+ * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n)
+ * but in sama5d2, the YCYC mode does not exist, and YYCC must be
+ * selected for both planar and interleaved modes, as in fact
+ * both modes are supported.
+ *
+ * Thus, if the YCYC mode is selected, replace it with the
+ * sama5d2-compliant mode which is YYCC .
+ */
+ if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) {
+ rlp_mode &= ~ISC_RLP_CFG_MODE_MASK;
+ rlp_mode |= ISC_RLP_CFG_MODE_YYCC;
+ }
+
+ regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
+ ISC_RLP_CFG_MODE_MASK, rlp_mode);
+}
+
+static void isc_sama5d2_adapt_pipeline(struct isc_device *isc)
+{
+ isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE;
+}
+
+/* Gamma table with gamma 1/2.2 */
+static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = {
+ /* 0 --> gamma 1/1.8 */
+ { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A,
+ 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
+ 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
+ 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
+ 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
+ 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
+ 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
+ 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
+ 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
+ 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
+ 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
+
+ /* 1 --> gamma 1/2 */
+ { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B,
+ 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
+ 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
+ 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
+ 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
+ 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
+ 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
+ 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
+ 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
+ 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
+ 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
+
+ /* 2 --> gamma 1/2.2 */
+ { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B,
+ 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
+ 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
+ 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
+ 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
+ 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
+ 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
+ 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
+ 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
+ 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
+ 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
+};
+
+static int isc_parse_dt(struct device *dev, struct isc_device *isc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *epn = NULL;
+ struct isc_subdev_entity *subdev_entity;
+ unsigned int flags;
+ int ret;
+
+ INIT_LIST_HEAD(&isc->subdev_entities);
+
+ while (1) {
+ struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
+
+ epn = of_graph_get_next_endpoint(np, epn);
+ if (!epn)
+ return 0;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
+ &v4l2_epn);
+ if (ret) {
+ ret = -EINVAL;
+ dev_err(dev, "Could not parse the endpoint\n");
+ break;
+ }
+
+ subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
+ GFP_KERNEL);
+ if (!subdev_entity) {
+ ret = -ENOMEM;
+ break;
+ }
+ subdev_entity->epn = epn;
+
+ flags = v4l2_epn.bus.parallel.flags;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
+
+ if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
+ ISC_PFE_CFG0_CCIR656;
+
+ list_add_tail(&subdev_entity->list, &isc->subdev_entities);
+ }
+ of_node_put(epn);
+
+ return ret;
+}
+
+static int atmel_isc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct isc_device *isc;
+ struct resource *res;
+ void __iomem *io_base;
+ struct isc_subdev_entity *subdev_entity;
+ int irq;
+ int ret;
+ u32 ver;
+
+ isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
+ if (!isc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, isc);
+ isc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ io_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
+ if (IS_ERR(isc->regmap)) {
+ ret = PTR_ERR(isc->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0,
+ "atmel-sama5d2-isc", isc);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ irq, ret);
+ return ret;
+ }
+
+ isc->gamma_table = isc_sama5d2_gamma_table;
+ isc->gamma_max = 2;
+
+ isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;
+ isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;
+
+ isc->config_dpc = isc_sama5d2_config_dpc;
+ isc->config_csc = isc_sama5d2_config_csc;
+ isc->config_cbc = isc_sama5d2_config_cbc;
+ isc->config_cc = isc_sama5d2_config_cc;
+ isc->config_gam = isc_sama5d2_config_gam;
+ isc->config_rlp = isc_sama5d2_config_rlp;
+ isc->config_ctrls = isc_sama5d2_config_ctrls;
+
+ isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;
+
+ isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;
+ isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;
+ isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;
+ isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;
+ isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;
+ isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;
+ isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;
+ isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;
+ isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;
+
+ isc->controller_formats = sama5d2_controller_formats;
+ isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);
+ isc->formats_list = sama5d2_formats_list;
+ isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);
+
+ /* sama5d2-isc - 8 bits per beat */
+ isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;
+
+ /* sama5d2-isc : ISPCK is required and mandatory */
+ isc->ispck_required = true;
+
+ ret = atmel_isc_pipeline_init(isc);
+ if (ret)
+ return ret;
+
+ isc->hclock = devm_clk_get(dev, "hclock");
+ if (IS_ERR(isc->hclock)) {
+ ret = PTR_ERR(isc->hclock);
+ dev_err(dev, "failed to get hclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(isc->hclock);
+ if (ret) {
+ dev_err(dev, "failed to enable hclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = atmel_isc_clk_init(isc);
+ if (ret) {
+ dev_err(dev, "failed to init isc clock: %d\n", ret);
+ goto unprepare_hclk;
+ }
+ ret = v4l2_device_register(dev, &isc->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "unable to register v4l2 device.\n");
+ goto unprepare_clk;
+ }
+
+ ret = isc_parse_dt(dev, isc);
+ if (ret) {
+ dev_err(dev, "fail to parse device tree\n");
+ goto unregister_v4l2_device;
+ }
+
+ if (list_empty(&isc->subdev_entities)) {
+ dev_err(dev, "no subdev found\n");
+ ret = -ENODEV;
+ goto unregister_v4l2_device;
+ }
+
+ list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
+ struct v4l2_async_subdev *asd;
+ struct fwnode_handle *fwnode =
+ of_fwnode_handle(subdev_entity->epn);
+
+ v4l2_async_nf_init(&subdev_entity->notifier);
+
+ asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
+ fwnode,
+ struct v4l2_async_subdev);
+
+ of_node_put(subdev_entity->epn);
+ subdev_entity->epn = NULL;
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto cleanup_subdev;
+ }
+
+ subdev_entity->notifier.ops = &atmel_isc_async_ops;
+
+ ret = v4l2_async_nf_register(&isc->v4l2_dev,
+ &subdev_entity->notifier);
+ if (ret) {
+ dev_err(dev, "fail to register async notifier\n");
+ goto cleanup_subdev;
+ }
+
+ if (video_is_registered(&isc->video_dev))
+ break;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_request_idle(dev);
+
+ isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
+
+ ret = clk_prepare_enable(isc->ispck);
+ if (ret) {
+ dev_err(dev, "failed to enable ispck: %d\n", ret);
+ goto disable_pm;
+ }
+
+ /* ispck should be greater or equal to hclock */
+ ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));
+ if (ret) {
+ dev_err(dev, "failed to set ispck rate: %d\n", ret);
+ goto unprepare_clk;
+ }
+
+ regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
+ dev_info(dev, "Microchip ISC version %x\n", ver);
+
+ return 0;
+
+unprepare_clk:
+ clk_disable_unprepare(isc->ispck);
+
+disable_pm:
+ pm_runtime_disable(dev);
+
+cleanup_subdev:
+ atmel_isc_subdev_cleanup(isc);
+
+unregister_v4l2_device:
+ v4l2_device_unregister(&isc->v4l2_dev);
+
+unprepare_hclk:
+ clk_disable_unprepare(isc->hclock);
+
+ atmel_isc_clk_cleanup(isc);
+
+ return ret;
+}
+
+static int atmel_isc_remove(struct platform_device *pdev)
+{
+ struct isc_device *isc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ atmel_isc_subdev_cleanup(isc);
+
+ v4l2_device_unregister(&isc->v4l2_dev);
+
+ clk_disable_unprepare(isc->ispck);
+ clk_disable_unprepare(isc->hclock);
+
+ atmel_isc_clk_cleanup(isc);
+
+ return 0;
+}
+
+static int __maybe_unused isc_runtime_suspend(struct device *dev)
+{
+ struct isc_device *isc = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(isc->ispck);
+ clk_disable_unprepare(isc->hclock);
+
+ return 0;
+}
+
+static int __maybe_unused isc_runtime_resume(struct device *dev)
+{
+ struct isc_device *isc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(isc->hclock);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(isc->ispck);
+ if (ret)
+ clk_disable_unprepare(isc->hclock);
+
+ return ret;
+}
+
+static const struct dev_pm_ops atmel_isc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id atmel_isc_of_match[] = {
+ { .compatible = "atmel,sama5d2-isc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
+#endif
+
+static struct platform_driver atmel_isc_driver = {
+ .probe = atmel_isc_probe,
+ .remove = atmel_isc_remove,
+ .driver = {
+ .name = "atmel-sama5d2-isc",
+ .pm = &atmel_isc_dev_pm_ops,
+ .of_match_table = of_match_ptr(atmel_isc_of_match),
+ },
+};
+
+module_platform_driver(atmel_isc_driver);
+
+MODULE_AUTHOR("Songjun Wu");
+MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c
new file mode 100644
index 000000000000..01ababdfcbd9
--- /dev/null
+++ b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip eXtended Image Sensor Controller (XISC) driver
+ *
+ * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
+ *
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
+ *
+ * ISC video pipeline integrates the following submodules:
+ * PFE: Parallel Front End to sample the camera sensor input stream
+ * DPC: Defective Pixel Correction with black offset correction, green disparity
+ * correction and defective pixel correction (3 modules total)
+ * WB: Programmable white balance in the Bayer domain
+ * CFA: Color filter array interpolation module
+ * CC: Programmable color correction
+ * GAM: Gamma correction
+ *VHXS: Vertical and Horizontal Scaler
+ * CSC: Programmable color space conversion
+ *CBHS: Contrast Brightness Hue and Saturation control
+ * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
+ * RLP: This module performs rounding, range limiting
+ * and packing of the incoming data
+ * DMA: This module performs DMA master accesses to write frames to external RAM
+ * HIS: Histogram module performs statistic counters on the frames
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "atmel-isc-regs.h"
+#include "atmel-isc.h"
+
+#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
+#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
+
+#define ISC_SAMA7G5_PIPELINE \
+ (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
+ CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
+
+/* This is a list of the formats that the ISC can *output* */
+static const struct isc_format sama7g5_controller_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ }, {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ },
+};
+
+/* This is a list of formats that the ISC can receive as *input* */
+static struct isc_format sama7g5_formats_list[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_GBGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_GRGR,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
+ .cfa_baycfg = ISC_BAY_CFG_RGRG,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ },
+};
+
+static void isc_sama7g5_config_csc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ /* Convert RGB to YUV */
+ regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
+ 0x42 | (0x81 << 16));
+ regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
+ 0x19 | (0x10 << 16));
+ regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
+ 0xFDA | (0xFB6 << 16));
+ regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
+ 0x70 | (0x80 << 16));
+ regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
+ 0x70 | (0xFA2 << 16));
+ regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
+ 0xFEE | (0x80 << 16));
+}
+
+static void isc_sama7g5_config_cbc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ /* Configure what is set via v4l2 ctrls */
+ regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
+ regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
+ /* Configure Hue and Saturation as neutral midpoint */
+ regmap_write(regmap, ISC_CBCHS_HUE, 0);
+ regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
+}
+
+static void isc_sama7g5_config_cc(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ /* Configure each register at the neutral fixed point 1.0 or 0.0 */
+ regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
+ regmap_write(regmap, ISC_CC_RB_OR, 0);
+ regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
+ regmap_write(regmap, ISC_CC_GB_OG, 0);
+ regmap_write(regmap, ISC_CC_BR_BG, 0);
+ regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
+}
+
+static void isc_sama7g5_config_ctrls(struct isc_device *isc,
+ const struct v4l2_ctrl_ops *ops)
+{
+ struct isc_ctrls *ctrls = &isc->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ ctrls->contrast = 16;
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
+}
+
+static void isc_sama7g5_config_dpc(struct isc_device *isc)
+{
+ u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
+ struct regmap *regmap = isc->regmap;
+
+ regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
+ (64 << ISC_DPC_CFG_BLOFF_SHIFT));
+ regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
+ (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
+}
+
+static void isc_sama7g5_config_gam(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+
+ regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
+ ISC_GAM_CTRL_BIPART);
+}
+
+static void isc_sama7g5_config_rlp(struct isc_device *isc)
+{
+ struct regmap *regmap = isc->regmap;
+ u32 rlp_mode = isc->config.rlp_cfg_mode;
+
+ regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
+ ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
+ ISC_RLP_CFG_YMODE_MASK, rlp_mode);
+}
+
+static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
+{
+ isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
+}
+
+/* Gamma table with gamma 1/2.2 */
+static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
+ /* index 0 --> gamma bipartite */
+ {
+ 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
+ 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
+ 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
+ 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
+ 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
+ 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
+ 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
+ 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
+ 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
+ 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
+ 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
+};
+
+static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *epn = NULL;
+ struct isc_subdev_entity *subdev_entity;
+ unsigned int flags;
+ int ret;
+ bool mipi_mode;
+
+ INIT_LIST_HEAD(&isc->subdev_entities);
+
+ mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
+
+ while (1) {
+ struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
+
+ epn = of_graph_get_next_endpoint(np, epn);
+ if (!epn)
+ return 0;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
+ &v4l2_epn);
+ if (ret) {
+ ret = -EINVAL;
+ dev_err(dev, "Could not parse the endpoint\n");
+ break;
+ }
+
+ subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
+ GFP_KERNEL);
+ if (!subdev_entity) {
+ ret = -ENOMEM;
+ break;
+ }
+ subdev_entity->epn = epn;
+
+ flags = v4l2_epn.bus.parallel.flags;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
+
+ if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
+ ISC_PFE_CFG0_CCIR656;
+
+ if (mipi_mode)
+ subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
+
+ list_add_tail(&subdev_entity->list, &isc->subdev_entities);
+ }
+ of_node_put(epn);
+
+ return ret;
+}
+
+static int microchip_xisc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct isc_device *isc;
+ struct resource *res;
+ void __iomem *io_base;
+ struct isc_subdev_entity *subdev_entity;
+ int irq;
+ int ret;
+ u32 ver;
+
+ isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
+ if (!isc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, isc);
+ isc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ io_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
+ if (IS_ERR(isc->regmap)) {
+ ret = PTR_ERR(isc->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0,
+ "microchip-sama7g5-xisc", isc);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ irq, ret);
+ return ret;
+ }
+
+ isc->gamma_table = isc_sama7g5_gamma_table;
+ isc->gamma_max = 0;
+
+ isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
+ isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
+
+ isc->config_dpc = isc_sama7g5_config_dpc;
+ isc->config_csc = isc_sama7g5_config_csc;
+ isc->config_cbc = isc_sama7g5_config_cbc;
+ isc->config_cc = isc_sama7g5_config_cc;
+ isc->config_gam = isc_sama7g5_config_gam;
+ isc->config_rlp = isc_sama7g5_config_rlp;
+ isc->config_ctrls = isc_sama7g5_config_ctrls;
+
+ isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
+
+ isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
+ isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
+ isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
+ isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
+ isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
+ isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
+ isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
+ isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
+ isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
+
+ isc->controller_formats = sama7g5_controller_formats;
+ isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
+ isc->formats_list = sama7g5_formats_list;
+ isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
+
+ /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
+ isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
+
+ /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
+ isc->ispck_required = false;
+
+ ret = atmel_isc_pipeline_init(isc);
+ if (ret)
+ return ret;
+
+ isc->hclock = devm_clk_get(dev, "hclock");
+ if (IS_ERR(isc->hclock)) {
+ ret = PTR_ERR(isc->hclock);
+ dev_err(dev, "failed to get hclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(isc->hclock);
+ if (ret) {
+ dev_err(dev, "failed to enable hclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = atmel_isc_clk_init(isc);
+ if (ret) {
+ dev_err(dev, "failed to init isc clock: %d\n", ret);
+ goto unprepare_hclk;
+ }
+
+ ret = v4l2_device_register(dev, &isc->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "unable to register v4l2 device.\n");
+ goto unprepare_hclk;
+ }
+
+ ret = xisc_parse_dt(dev, isc);
+ if (ret) {
+ dev_err(dev, "fail to parse device tree\n");
+ goto unregister_v4l2_device;
+ }
+
+ if (list_empty(&isc->subdev_entities)) {
+ dev_err(dev, "no subdev found\n");
+ ret = -ENODEV;
+ goto unregister_v4l2_device;
+ }
+
+ list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
+ struct v4l2_async_subdev *asd;
+ struct fwnode_handle *fwnode =
+ of_fwnode_handle(subdev_entity->epn);
+
+ v4l2_async_nf_init(&subdev_entity->notifier);
+
+ asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
+ fwnode,
+ struct v4l2_async_subdev);
+
+ of_node_put(subdev_entity->epn);
+ subdev_entity->epn = NULL;
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto cleanup_subdev;
+ }
+
+ subdev_entity->notifier.ops = &atmel_isc_async_ops;
+
+ ret = v4l2_async_nf_register(&isc->v4l2_dev,
+ &subdev_entity->notifier);
+ if (ret) {
+ dev_err(dev, "fail to register async notifier\n");
+ goto cleanup_subdev;
+ }
+
+ if (video_is_registered(&isc->video_dev))
+ break;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_request_idle(dev);
+
+ regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
+ dev_info(dev, "Microchip XISC version %x\n", ver);
+
+ return 0;
+
+cleanup_subdev:
+ atmel_isc_subdev_cleanup(isc);
+
+unregister_v4l2_device:
+ v4l2_device_unregister(&isc->v4l2_dev);
+
+unprepare_hclk:
+ clk_disable_unprepare(isc->hclock);
+
+ atmel_isc_clk_cleanup(isc);
+
+ return ret;
+}
+
+static int microchip_xisc_remove(struct platform_device *pdev)
+{
+ struct isc_device *isc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ atmel_isc_subdev_cleanup(isc);
+
+ v4l2_device_unregister(&isc->v4l2_dev);
+
+ clk_disable_unprepare(isc->hclock);
+
+ atmel_isc_clk_cleanup(isc);
+
+ return 0;
+}
+
+static int __maybe_unused xisc_runtime_suspend(struct device *dev)
+{
+ struct isc_device *isc = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(isc->hclock);
+
+ return 0;
+}
+
+static int __maybe_unused xisc_runtime_resume(struct device *dev)
+{
+ struct isc_device *isc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(isc->hclock);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id microchip_xisc_of_match[] = {
+ { .compatible = "microchip,sama7g5-isc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
+#endif
+
+static struct platform_driver microchip_xisc_driver = {
+ .probe = microchip_xisc_probe,
+ .remove = microchip_xisc_remove,
+ .driver = {
+ .name = "microchip-sama7g5-xisc",
+ .pm = &microchip_xisc_dev_pm_ops,
+ .of_match_table = of_match_ptr(microchip_xisc_of_match),
+ },
+};
+
+module_platform_driver(microchip_xisc_driver);
+
+MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
+MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/deprecated/stkwebcam/Kconfig b/drivers/staging/media/deprecated/stkwebcam/Kconfig
index 4450403dff41..7234498e634a 100644
--- a/drivers/staging/media/deprecated/stkwebcam/Kconfig
+++ b/drivers/staging/media/deprecated/stkwebcam/Kconfig
@@ -2,7 +2,7 @@
config VIDEO_STKWEBCAM
tristate "USB Syntek DC1125 Camera support (DEPRECATED)"
depends on VIDEO_DEV
- depends on USB
+ depends on MEDIA_USB_SUPPORT && MEDIA_CAMERA_SUPPORT
help
Say Y here if you want to use this type of camera.
Supported devices are typically found in some Asus laptops,
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 0bacac302d7e..21fd79515042 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -23,12 +23,15 @@ config VIDEO_IMX_CSI
default y
help
A video4linux camera sensor interface driver for i.MX5/6.
-
-config VIDEO_IMX7_CSI
- tristate "i.MX6UL/L / i.MX7 / i.MX8M Camera Sensor Interface driver"
- default y
- help
- Enable support for video4linux camera sensor interface driver for
- i.MX6UL/L, i.MX7 or i.MX8M.
endmenu
endif
+
+config VIDEO_IMX8MQ_MIPI_CSI2
+ tristate "NXP i.MX8MQ MIPI CSI-2 receiver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ V4L2 driver for the MIPI CSI-2 receiver found in the i.MX8MQ SoC.
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index d82be898145b..906a422aa656 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -14,5 +14,4 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-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) += imx8mq-mipi-csi2.o
+obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO
index 5d3a337c8702..11c9e10d34ae 100644
--- a/drivers/staging/media/imx/TODO
+++ b/drivers/staging/media/imx/TODO
@@ -2,18 +2,6 @@
- The Frame Interval Monitor could be exported to v4l2-core for
general use.
-- The CSI subdevice parses its nearest upstream neighbor's device-tree
- bus config in order to setup the CSI. Laurent Pinchart argues that
- instead the CSI subdev should call its neighbor's g_mbus_config op
- (which should be propagated if necessary) to get this info. However
- Hans Verkuil is planning to remove the g_mbus_config op. For now this
- driver uses the parsed DT bus config method until this issue is
- resolved.
-
- 2020-06: g_mbus has been removed in favour of the get_mbus_config pad
- operation which should be used to avoid parsing the remote endpoint
- configuration.
-
- This media driver supports inheriting V4L2 controls to the
video capture devices, from the subdevices in the capture device's
pipeline. The controls for each capture device are updated in the
@@ -23,32 +11,3 @@
- Similarly to the legacy control handling, legacy format handling where
formats on the video nodes are influenced by the active format of the
connected subdev should be removed.
-
-- i.MX7: all of the above, since it uses the imx media core
-
-- i.MX7: use Frame Interval Monitor
-
-- imx7-media-csi: Restrict the supported formats list to the SoC version.
-
- The imx7 CSI bridge can be configured to sample pixel components from the Rx
- queue in single (8bpp) or double (16bpp) component modes. Image format
- variants with different sample sizes (ie YUYV_2X8 vs YUYV_1X16) determine the
- pixel components sampling size per each clock cycle and their packing mode
- (see imx7_csi_configure() for details).
-
- As the imx7 CSI bridge can be interfaced with different IP blocks depending on
- the SoC model it is integrated on, the Rx queue sampling size should match
- the size of the samples transferred by the transmitting IP block.
-
- To avoid mis-configurations of the capture pipeline, the enumeration of the
- supported formats should be restricted to match the pixel source transmitting
- mode.
-
- Example: i.MX8MM SoC integrates the CSI bridge with the Samsung CSIS CSI-2
- receiver which operates in dual pixel sampling mode. The CSI bridge should
- only expose the 1X16 formats variant which instructs it to operate in dual
- pixel sampling mode. When the CSI bridge is instead integrated on an i.MX7,
- which supports both serial and parallel input, it should expose both variants.
-
- This currently only applies to YUYV formats, but other formats might need
- to be handled in the same way.
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index b2b1f4dd41d7..5c3cc7de209d 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -97,8 +97,8 @@ struct csi_priv {
/* the mipi virtual channel number at link validate */
int vc_num;
- /* the upstream endpoint CSI is receiving from */
- struct v4l2_fwnode_endpoint upstream_ep;
+ /* media bus config of the upstream subdevice CSI is receiving from */
+ struct v4l2_mbus_config mbus_cfg;
spinlock_t irqlock; /* protect eof_irq handler */
struct timer_list eof_timeout_timer;
@@ -125,14 +125,14 @@ static inline struct csi_priv *notifier_to_dev(struct v4l2_async_notifier *n)
return container_of(n, struct csi_priv, notifier);
}
-static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep)
+static inline bool is_parallel_bus(struct v4l2_mbus_config *mbus_cfg)
{
- return ep->bus_type != V4L2_MBUS_CSI2_DPHY;
+ return mbus_cfg->type != V4L2_MBUS_CSI2_DPHY;
}
-static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
+static inline bool is_parallel_16bit_bus(struct v4l2_mbus_config *mbus_cfg)
{
- return is_parallel_bus(ep) && ep->bus.parallel.bus_width >= 16;
+ return is_parallel_bus(mbus_cfg) && mbus_cfg->bus.parallel.bus_width >= 16;
}
/*
@@ -145,36 +145,31 @@ static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
* - the CSI is receiving from an 8-bit parallel bus and the incoming
* media bus format is other than UYVY8_2X8/YUYV8_2X8.
*/
-static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep,
+static inline bool requires_passthrough(struct v4l2_mbus_config *mbus_cfg,
struct v4l2_mbus_framefmt *infmt,
const struct imx_media_pixfmt *incc)
{
- if (ep->bus_type == V4L2_MBUS_BT656) // including BT.1120
+ if (mbus_cfg->type == V4L2_MBUS_BT656) // including BT.1120
return false;
- return incc->bayer || is_parallel_16bit_bus(ep) ||
- (is_parallel_bus(ep) &&
+ return incc->bayer || is_parallel_16bit_bus(mbus_cfg) ||
+ (is_parallel_bus(mbus_cfg) &&
infmt->code != MEDIA_BUS_FMT_UYVY8_2X8 &&
infmt->code != MEDIA_BUS_FMT_YUYV8_2X8);
}
/*
- * Parses the fwnode endpoint from the source pad of the entity
- * connected to this CSI. This will either be the entity directly
- * upstream from the CSI-2 receiver, directly upstream from the
- * video mux, or directly upstream from the CSI itself. The endpoint
- * is needed to determine the bus type and bus config coming into
- * the CSI.
+ * Queries the media bus config of the upstream entity that provides data to
+ * the CSI. This will either be the entity directly upstream from the CSI-2
+ * receiver, directly upstream from a video mux, or directly upstream from
+ * the CSI itself.
*/
-static int csi_get_upstream_endpoint(struct csi_priv *priv,
- struct v4l2_fwnode_endpoint *ep)
+static int csi_get_upstream_mbus_config(struct csi_priv *priv,
+ struct v4l2_mbus_config *mbus_cfg)
{
- struct fwnode_handle *endpoint;
- struct v4l2_subdev *sd;
- struct media_pad *pad;
-
- if (!IS_ENABLED(CONFIG_OF))
- return -ENXIO;
+ struct v4l2_subdev *sd, *remote_sd;
+ struct media_pad *remote_pad;
+ int ret;
if (!priv->src_sd)
return -EPIPE;
@@ -206,19 +201,21 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv,
}
/* get source pad of entity directly upstream from sd */
- pad = imx_media_pipeline_pad(&sd->entity, 0, 0, true);
- if (!pad)
- return -ENODEV;
-
- endpoint = imx_media_get_pad_fwnode(pad);
- if (IS_ERR(endpoint))
- return PTR_ERR(endpoint);
+ remote_pad = media_entity_remote_pad_unique(&sd->entity,
+ MEDIA_PAD_FL_SOURCE);
+ if (IS_ERR(remote_pad))
+ return PTR_ERR(remote_pad);
- v4l2_fwnode_endpoint_parse(endpoint, ep);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- fwnode_handle_put(endpoint);
+ ret = v4l2_subdev_call(remote_sd, pad, get_mbus_config,
+ remote_pad->index, mbus_cfg);
+ if (ret == -ENOIOCTLCMD)
+ v4l2_err(&priv->sd,
+ "entity %s does not implement get_mbus_config()\n",
+ remote_pad->entity->name);
- return 0;
+ return ret;
}
static void csi_idmac_put_ipu_resources(struct csi_priv *priv)
@@ -435,7 +432,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
image.phys0 = phys[0];
image.phys1 = phys[1];
- passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc);
+ passthrough = requires_passthrough(&priv->mbus_cfg, infmt, incc);
passthrough_cycles = 1;
/*
@@ -708,7 +705,6 @@ static int csi_setup(struct csi_priv *priv)
{
struct v4l2_mbus_framefmt *infmt, *outfmt;
const struct imx_media_pixfmt *incc;
- struct v4l2_mbus_config mbus_cfg;
struct v4l2_mbus_framefmt if_fmt;
struct v4l2_rect crop;
@@ -716,13 +712,6 @@ static int csi_setup(struct csi_priv *priv)
incc = priv->cc[CSI_SINK_PAD];
outfmt = &priv->format_mbus[priv->active_output_pad];
- /* compose mbus_config from the upstream endpoint */
- mbus_cfg.type = priv->upstream_ep.bus_type;
- if (is_parallel_bus(&priv->upstream_ep))
- mbus_cfg.bus.parallel = priv->upstream_ep.bus.parallel;
- else
- mbus_cfg.bus.mipi_csi2 = priv->upstream_ep.bus.mipi_csi2;
-
if_fmt = *infmt;
crop = priv->crop;
@@ -730,7 +719,7 @@ static int csi_setup(struct csi_priv *priv)
* if cycles is set, we need to handle this over multiple cycles as
* generic/bayer data
*/
- if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) {
+ if (is_parallel_bus(&priv->mbus_cfg) && incc->cycles) {
if_fmt.width *= incc->cycles;
crop.width *= incc->cycles;
}
@@ -741,7 +730,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, outfmt);
+ ipu_csi_init_interface(priv->csi, &priv->mbus_cfg, &if_fmt, outfmt);
ipu_csi_set_dest(priv->csi, priv->dest);
@@ -769,7 +758,7 @@ static int csi_start(struct csi_priv *priv)
return ret;
/* Skip first few frames from a BT.656 source */
- if (priv->upstream_ep.bus_type == V4L2_MBUS_BT656) {
+ if (priv->mbus_cfg.type == V4L2_MBUS_BT656) {
u32 delay_usec, bad_frames = 20;
delay_usec = DIV_ROUND_UP_ULL((u64)USEC_PER_SEC *
@@ -1118,7 +1107,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sink_fmt)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
+ struct v4l2_mbus_config mbus_cfg = { .type = 0 };
bool is_csi2;
int ret;
@@ -1127,16 +1116,17 @@ static int csi_link_validate(struct v4l2_subdev *sd,
if (ret)
return ret;
- ret = csi_get_upstream_endpoint(priv, &upstream_ep);
+ ret = csi_get_upstream_mbus_config(priv, &mbus_cfg);
if (ret) {
- v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
+ v4l2_err(&priv->sd,
+ "failed to get upstream media bus configuration\n");
return ret;
}
mutex_lock(&priv->lock);
- priv->upstream_ep = upstream_ep;
- is_csi2 = !is_parallel_bus(&upstream_ep);
+ priv->mbus_cfg = mbus_cfg;
+ is_csi2 = !is_parallel_bus(&mbus_cfg);
if (is_csi2) {
/*
* NOTE! It seems the virtual channels from the mipi csi-2
@@ -1192,7 +1182,7 @@ static void csi_try_crop(struct csi_priv *priv,
struct v4l2_rect *crop,
struct v4l2_subdev_state *sd_state,
struct v4l2_mbus_framefmt *infmt,
- struct v4l2_fwnode_endpoint *upstream_ep)
+ struct v4l2_mbus_config *mbus_cfg)
{
u32 in_height;
@@ -1216,7 +1206,7 @@ static void csi_try_crop(struct csi_priv *priv,
* sync, so fix it to NTSC/PAL active lines. NTSC contains
* 2 extra lines of active video that need to be cropped.
*/
- if (upstream_ep->bus_type == V4L2_MBUS_BT656 &&
+ if (mbus_cfg->type == V4L2_MBUS_BT656 &&
(V4L2_FIELD_HAS_BOTH(infmt->field) ||
infmt->field == V4L2_FIELD_ALTERNATE)) {
crop->height = in_height;
@@ -1233,7 +1223,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum *code)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
+ struct v4l2_mbus_config mbus_cfg = { .type = 0 };
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
int ret = 0;
@@ -1250,13 +1240,14 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
break;
case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC:
- ret = csi_get_upstream_endpoint(priv, &upstream_ep);
+ ret = csi_get_upstream_mbus_config(priv, &mbus_cfg);
if (ret) {
- v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
+ v4l2_err(&priv->sd,
+ "failed to get upstream media bus configuration\n");
goto out;
}
- if (requires_passthrough(&upstream_ep, infmt, incc)) {
+ if (requires_passthrough(&mbus_cfg, infmt, incc)) {
if (code->index != 0) {
ret = -EINVAL;
goto out;
@@ -1426,7 +1417,7 @@ static void csi_try_field(struct csi_priv *priv,
}
static void csi_try_fmt(struct csi_priv *priv,
- struct v4l2_fwnode_endpoint *upstream_ep,
+ struct v4l2_mbus_config *mbus_cfg,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat,
struct v4l2_rect *crop,
@@ -1447,7 +1438,7 @@ static void csi_try_fmt(struct csi_priv *priv,
sdformat->format.width = compose->width;
sdformat->format.height = compose->height;
- if (requires_passthrough(upstream_ep, infmt, incc)) {
+ if (requires_passthrough(mbus_cfg, infmt, incc)) {
sdformat->format.code = infmt->code;
*cc = incc;
} else {
@@ -1497,8 +1488,7 @@ static void csi_try_fmt(struct csi_priv *priv,
crop->height = sdformat->format.height;
if (sdformat->format.field == V4L2_FIELD_ALTERNATE)
crop->height *= 2;
- csi_try_crop(priv, crop, sd_state, &sdformat->format,
- upstream_ep);
+ csi_try_crop(priv, crop, sd_state, &sdformat->format, mbus_cfg);
compose->left = 0;
compose->top = 0;
compose->width = crop->width;
@@ -1516,7 +1506,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sdformat)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
+ struct v4l2_mbus_config mbus_cfg = { .type = 0 };
const struct imx_media_pixfmt *cc;
struct v4l2_mbus_framefmt *fmt;
struct v4l2_rect *crop, *compose;
@@ -1525,9 +1515,10 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
if (sdformat->pad >= CSI_NUM_PADS)
return -EINVAL;
- ret = csi_get_upstream_endpoint(priv, &upstream_ep);
+ ret = csi_get_upstream_mbus_config(priv, &mbus_cfg);
if (ret) {
- v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
+ v4l2_err(&priv->sd,
+ "failed to get upstream media bus configuration\n");
return ret;
}
@@ -1541,8 +1532,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
crop = __csi_get_crop(priv, sd_state, sdformat->which);
compose = __csi_get_compose(priv, sd_state, sdformat->which);
- csi_try_fmt(priv, &upstream_ep, sd_state, sdformat, crop, compose,
- &cc);
+ csi_try_fmt(priv, &mbus_cfg, sd_state, sdformat, crop, compose, &cc);
fmt = __csi_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
*fmt = sdformat->format;
@@ -1559,8 +1549,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
format.pad = pad;
format.which = sdformat->which;
format.format = sdformat->format;
- csi_try_fmt(priv, &upstream_ep, sd_state, &format,
- NULL, compose, &outcc);
+ csi_try_fmt(priv, &mbus_cfg, sd_state, &format, NULL,
+ compose, &outcc);
outfmt = __csi_get_fmt(priv, sd_state, pad,
sdformat->which);
@@ -1648,7 +1638,7 @@ static int csi_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_selection *sel)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
+ struct v4l2_mbus_config mbus_cfg = { .type = 0 };
struct v4l2_mbus_framefmt *infmt;
struct v4l2_rect *crop, *compose;
int pad, ret;
@@ -1656,9 +1646,10 @@ static int csi_set_selection(struct v4l2_subdev *sd,
if (sel->pad != CSI_SINK_PAD)
return -EINVAL;
- ret = csi_get_upstream_endpoint(priv, &upstream_ep);
+ ret = csi_get_upstream_mbus_config(priv, &mbus_cfg);
if (ret) {
- v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
+ v4l2_err(&priv->sd,
+ "failed to get upstream media bus configuration\n");
return ret;
}
@@ -1687,7 +1678,7 @@ static int csi_set_selection(struct v4l2_subdev *sd,
goto out;
}
- csi_try_crop(priv, &sel->r, sd_state, infmt, &upstream_ep);
+ csi_try_crop(priv, &sel->r, sd_state, infmt, &mbus_cfg);
*crop = sel->r;
diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c
index 3a9182933508..fb6590dcfc36 100644
--- a/drivers/staging/media/imx/imx-media-fim.c
+++ b/drivers/staging/media/imx/imx-media-fim.c
@@ -187,54 +187,6 @@ out_update_ts:
send_fim_event(fim, error_avg);
}
-#ifdef CONFIG_IMX_GPT_ICAP
-/*
- * Input Capture method of measuring frame intervals. Not subject
- * to interrupt latency.
- */
-static void fim_input_capture_handler(int channel, void *dev_id,
- ktime_t timestamp)
-{
- struct imx_media_fim *fim = dev_id;
- unsigned long flags;
-
- spin_lock_irqsave(&fim->lock, flags);
-
- frame_interval_monitor(fim, timestamp);
-
- if (!completion_done(&fim->icap_first_event))
- complete(&fim->icap_first_event);
-
- spin_unlock_irqrestore(&fim->lock, flags);
-}
-
-static int fim_request_input_capture(struct imx_media_fim *fim)
-{
- init_completion(&fim->icap_first_event);
-
- return mxc_request_input_capture(fim->icap_channel,
- fim_input_capture_handler,
- fim->icap_flags, fim);
-}
-
-static void fim_free_input_capture(struct imx_media_fim *fim)
-{
- mxc_free_input_capture(fim->icap_channel, fim);
-}
-
-#else /* CONFIG_IMX_GPT_ICAP */
-
-static int fim_request_input_capture(struct imx_media_fim *fim)
-{
- return 0;
-}
-
-static void fim_free_input_capture(struct imx_media_fim *fim)
-{
-}
-
-#endif /* CONFIG_IMX_GPT_ICAP */
-
/*
* In case we are monitoring the first frame interval after streamon
* (when fim->num_skip = 0), we need a valid fim->last_ts before we
@@ -434,15 +386,8 @@ int imx_media_fim_set_stream(struct imx_media_fim *fim,
update_fim_nominal(fim, fi);
spin_unlock_irqrestore(&fim->lock, flags);
- if (icap_enabled(fim)) {
- ret = fim_request_input_capture(fim);
- if (ret)
- goto out;
- fim_acquire_first_ts(fim);
- }
- } else {
if (icap_enabled(fim))
- fim_free_input_capture(fim);
+ fim_acquire_first_ts(fim);
}
fim->stream_on = on;
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index 3e7462112649..411e907b68eb 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -814,39 +814,6 @@ imx_media_pipeline_video_device(struct media_entity *start_entity,
EXPORT_SYMBOL_GPL(imx_media_pipeline_video_device);
/*
- * Find a fwnode endpoint that maps to the given subdevice's pad.
- * If there are multiple endpoints that map to the pad, only the
- * first endpoint encountered is returned.
- *
- * On success the refcount of the returned fwnode endpoint is
- * incremented.
- */
-struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad)
-{
- struct fwnode_handle *endpoint;
- struct v4l2_subdev *sd;
-
- if (!is_media_entity_v4l2_subdev(pad->entity))
- return ERR_PTR(-ENODEV);
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- fwnode_graph_for_each_endpoint(dev_fwnode(sd->dev), endpoint) {
- int pad_idx = media_entity_get_fwnode_pad(&sd->entity,
- endpoint,
- pad->flags);
- if (pad_idx < 0)
- continue;
-
- if (pad_idx == pad->index)
- return endpoint;
- }
-
- return ERR_PTR(-ENODEV);
-}
-EXPORT_SYMBOL_GPL(imx_media_get_pad_fwnode);
-
-/*
* Turn current pipeline streaming on/off starting from entity.
*/
int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index f263fc3adbb9..f679249d82e4 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -219,7 +219,6 @@ imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id,
struct video_device *
imx_media_pipeline_video_device(struct media_entity *start_entity,
enum v4l2_buf_type buftype, bool upstream);
-struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad);
struct imx_media_dma_buf {
void *virt;
diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c
deleted file mode 100644
index e5b550ccfa22..000000000000
--- a/drivers/staging/media/imx/imx7-media-csi.c
+++ /dev/null
@@ -1,2308 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / 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_device.h>
-#include <linux/of_graph.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-#include <media/v4l2-device.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mc.h>
-#include <media/v4l2-subdev.h>
-#include <media/videobuf2-dma-contig.h>
-
-#define IMX7_CSI_PAD_SINK 0
-#define IMX7_CSI_PAD_SRC 1
-#define IMX7_CSI_PADS_NUM 2
-
-/* 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(n) ((n) << 12)
-#define BIT_MCLKDIV_MASK (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)
-
-/* control reg 2 */
-#define BIT_DMA_BURST_TYPE_RFF_INCR4 (1 << 30)
-#define BIT_DMA_BURST_TYPE_RFF_INCR8 (2 << 30)
-#define BIT_DMA_BURST_TYPE_RFF_INCR16 (3 << 30)
-#define BIT_DMA_BURST_TYPE_RFF_MASK (3 << 30)
-
-/* control reg 3 */
-#define BIT_FRMCNT(n) ((n) << 16)
-#define BIT_FRMCNT_MASK (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(n) ((n) << 8)
-#define BIT_STATFF_LEVEL_MASK (0x7 << 8)
-#define BIT_HRESP_ERR_EN BIT(7)
-#define BIT_RXFF_LEVEL(n) ((n) << 4)
-#define BIT_RXFF_LEVEL_MASK (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)
-
-/* 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 image parameter reg */
-#define BIT_IMAGE_WIDTH(n) ((n) << 16)
-#define BIT_IMAGE_HEIGHT(n) (n)
-
-/* 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_DATA_FROM_MIPI BIT(22)
-#define BIT_MIPI_YU_SWAP BIT(21)
-#define BIT_MIPI_DOUBLE_CMPNT BIT(20)
-#define BIT_MASK_OPTION_FIRST_FRAME (0 << 18)
-#define BIT_MASK_OPTION_CSI_EN (1 << 18)
-#define BIT_MASK_OPTION_SECOND_FRAME (2 << 18)
-#define BIT_MASK_OPTION_ON_DATA (3 << 18)
-#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 0x00
-#define CSI_CSICR2 0x04
-#define CSI_CSICR3 0x08
-#define CSI_STATFIFO 0x0c
-#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
-
-#define IMX7_CSI_VIDEO_NAME "imx-capture"
-/* In bytes, per queue */
-#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_512M
-#define IMX7_CSI_VIDEO_EOF_TIMEOUT 2000
-
-#define IMX7_CSI_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_2X8
-#define IMX7_CSI_DEF_PIX_FORMAT V4L2_PIX_FMT_UYVY
-#define IMX7_CSI_DEF_PIX_WIDTH 640
-#define IMX7_CSI_DEF_PIX_HEIGHT 480
-
-enum imx_csi_model {
- IMX7_CSI_IMX7 = 0,
- IMX7_CSI_IMX8MQ,
-};
-
-struct imx7_csi_pixfmt {
- /* the in-memory FourCC pixel format */
- u32 fourcc;
- /*
- * the set of equivalent media bus codes for the fourcc.
- * NOTE! codes pointer is NULL for in-memory-only formats.
- */
- const u32 *codes;
- int bpp; /* total bpp */
- bool yuv;
-};
-
-struct imx7_csi_vb2_buffer {
- struct vb2_v4l2_buffer vbuf;
- struct list_head list;
-};
-
-static inline struct imx7_csi_vb2_buffer *
-to_imx7_csi_vb2_buffer(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
-
- return container_of(vbuf, struct imx7_csi_vb2_buffer, vbuf);
-}
-
-struct imx7_csi_dma_buf {
- void *virt;
- dma_addr_t phys;
- unsigned long len;
-};
-
-struct imx7_csi {
- struct device *dev;
-
- /* Resources and locks */
- void __iomem *regbase;
- int irq;
- struct clk *mclk;
-
- struct mutex lock; /* Protects is_streaming, format_mbus, cc */
- spinlock_t irqlock; /* Protects last_eof */
-
- /* Media and V4L2 device */
- struct media_device mdev;
- struct v4l2_device v4l2_dev;
- struct v4l2_async_notifier notifier;
- struct media_pipeline pipe;
-
- struct v4l2_subdev *src_sd;
- bool is_csi2;
-
- /* V4L2 subdev */
- struct v4l2_subdev sd;
- struct media_pad pad[IMX7_CSI_PADS_NUM];
-
- struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM];
- const struct imx7_csi_pixfmt *cc[IMX7_CSI_PADS_NUM];
-
- /* Video device */
- struct video_device *vdev; /* Video device */
- struct media_pad vdev_pad; /* Video device pad */
-
- struct v4l2_pix_format vdev_fmt; /* The user format */
- const struct imx7_csi_pixfmt *vdev_cc;
- struct v4l2_rect vdev_compose; /* The compose rectangle */
-
- struct mutex vdev_mutex; /* Protect vdev operations */
-
- struct vb2_queue q; /* The videobuf2 queue */
- struct list_head ready_q; /* List of queued buffers */
- spinlock_t q_lock; /* Protect ready_q */
-
- /* Buffers and streaming state */
- struct imx7_csi_vb2_buffer *active_vb2_buf[2];
- struct imx7_csi_dma_buf underrun_buf;
-
- bool is_streaming;
- int buf_num;
- u32 frame_sequence;
-
- bool last_eof;
- struct completion last_eof_completion;
-
- enum imx_csi_model model;
-};
-
-static struct imx7_csi *
-imx7_csi_notifier_to_dev(struct v4l2_async_notifier *n)
-{
- return container_of(n, struct imx7_csi, notifier);
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware Configuration
- */
-
-static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset)
-{
- return readl(csi->regbase + offset);
-}
-
-static void imx7_csi_reg_write(struct imx7_csi *csi, unsigned int value,
- unsigned int offset)
-{
- writel(value, csi->regbase + offset);
-}
-
-static u32 imx7_csi_irq_clear(struct imx7_csi *csi)
-{
- u32 isr;
-
- isr = imx7_csi_reg_read(csi, CSI_CSISR);
- imx7_csi_reg_write(csi, isr, CSI_CSISR);
-
- return isr;
-}
-
-static void imx7_csi_init_default(struct imx7_csi *csi)
-{
- imx7_csi_reg_write(csi, BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE |
- BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) |
- BIT_MCLKEN, CSI_CSICR1);
- imx7_csi_reg_write(csi, 0, CSI_CSICR2);
- imx7_csi_reg_write(csi, BIT_FRMCNT_RST, CSI_CSICR3);
-
- imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(IMX7_CSI_DEF_PIX_WIDTH) |
- BIT_IMAGE_HEIGHT(IMX7_CSI_DEF_PIX_HEIGHT),
- CSI_CSIIMAG_PARA);
-
- imx7_csi_reg_write(csi, BIT_DMA_REFLASH_RFF, CSI_CSICR3);
-}
-
-static void imx7_csi_hw_enable_irq(struct imx7_csi *csi)
-{
- u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
-
- cr1 |= BIT_RFF_OR_INT;
- cr1 |= BIT_FB1_DMA_DONE_INTEN;
- cr1 |= BIT_FB2_DMA_DONE_INTEN;
-
- imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
-}
-
-static void imx7_csi_hw_disable_irq(struct imx7_csi *csi)
-{
- u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
-
- cr1 &= ~BIT_RFF_OR_INT;
- cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
- cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
-
- imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
-}
-
-static void imx7_csi_hw_enable(struct imx7_csi *csi)
-{
- u32 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)
-{
- u32 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)
-{
- u32 cr3;
-
- 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)
-{
- u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1) & ~BIT_FCC;
-
- imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
- imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
- imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1);
-}
-
-static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi)
-{
- u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
-
- cr3 |= BIT_DMA_REQ_EN_RFF;
- cr3 |= BIT_HRESP_ERR_EN;
- cr3 &= ~BIT_RXFF_LEVEL_MASK;
- cr3 |= BIT_RXFF_LEVEL(2);
-
- imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
-}
-
-static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi)
-{
- u32 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_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 struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi);
-
-static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi)
-{
- struct imx7_csi_vb2_buffer *buf;
- struct vb2_buffer *vb2_buf;
- dma_addr_t phys[2];
- int i;
-
- for (i = 0; i < 2; i++) {
- buf = imx7_csi_video_next_buf(csi);
- 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 imx7_csi_vb2_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);
- csi->active_vb2_buf[i] = NULL;
- }
- }
-}
-
-static void imx7_csi_free_dma_buf(struct imx7_csi *csi,
- struct imx7_csi_dma_buf *buf)
-{
- if (buf->virt)
- dma_free_coherent(csi->dev, buf->len, buf->virt, buf->phys);
-
- buf->virt = NULL;
- buf->phys = 0;
-}
-
-static int imx7_csi_alloc_dma_buf(struct imx7_csi *csi,
- struct imx7_csi_dma_buf *buf, int size)
-{
- imx7_csi_free_dma_buf(csi, buf);
-
- buf->len = PAGE_ALIGN(size);
- buf->virt = dma_alloc_coherent(csi->dev, buf->len, &buf->phys,
- GFP_DMA | GFP_KERNEL);
- if (!buf->virt)
- return -ENOMEM;
-
- return 0;
-}
-
-static int imx7_csi_dma_setup(struct imx7_csi *csi)
-{
- int ret;
-
- ret = imx7_csi_alloc_dma_buf(csi, &csi->underrun_buf,
- csi->vdev_fmt.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_cleanup(struct imx7_csi *csi,
- enum vb2_buffer_state return_status)
-{
- imx7_csi_dma_unsetup_vb2_buf(csi, return_status);
- imx7_csi_free_dma_buf(csi, &csi->underrun_buf);
-}
-
-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(IMX7_CSI_VIDEO_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);
-}
-
-static void imx7_csi_configure(struct imx7_csi *csi)
-{
- struct v4l2_pix_format *out_pix = &csi->vdev_fmt;
- int width = out_pix->width;
- u32 stride = 0;
- u32 cr3 = BIT_FRMCNT_RST;
- u32 cr1, cr18;
-
- cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
-
- cr18 &= ~(BIT_CSI_HW_ENABLE | BIT_MIPI_DATA_FORMAT_MASK |
- BIT_DATA_FROM_MIPI | BIT_BASEADDR_CHG_ERR_EN |
- BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL |
- BIT_DEINTERLACE_EN);
-
- if (out_pix->field == V4L2_FIELD_INTERLACED) {
- cr18 |= BIT_DEINTERLACE_EN;
- stride = out_pix->width;
- }
-
- if (!csi->is_csi2) {
- cr1 = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL
- | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN;
-
- cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL |
- BIT_BASEADDR_CHG_ERR_EN;
-
- if (out_pix->pixelformat == V4L2_PIX_FMT_UYVY ||
- out_pix->pixelformat == V4L2_PIX_FMT_YUYV)
- width *= 2;
- } else {
- cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC
- | BIT_MCLKDIV(1) | BIT_MCLKEN;
-
- cr18 |= BIT_DATA_FROM_MIPI;
-
- switch (csi->format_mbus[IMX7_CSI_PAD_SINK].code) {
- case MEDIA_BUS_FMT_Y8_1X8:
- case MEDIA_BUS_FMT_SBGGR8_1X8:
- case MEDIA_BUS_FMT_SGBRG8_1X8:
- case MEDIA_BUS_FMT_SGRBG8_1X8:
- case MEDIA_BUS_FMT_SRGGB8_1X8:
- cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
- break;
- case MEDIA_BUS_FMT_Y10_1X10:
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- case MEDIA_BUS_FMT_SGBRG10_1X10:
- case MEDIA_BUS_FMT_SGRBG10_1X10:
- case MEDIA_BUS_FMT_SRGGB10_1X10:
- cr3 |= BIT_TWO_8BIT_SENSOR;
- cr18 |= BIT_MIPI_DATA_FORMAT_RAW10;
- break;
- case MEDIA_BUS_FMT_Y12_1X12:
- case MEDIA_BUS_FMT_SBGGR12_1X12:
- case MEDIA_BUS_FMT_SGBRG12_1X12:
- case MEDIA_BUS_FMT_SGRBG12_1X12:
- case MEDIA_BUS_FMT_SRGGB12_1X12:
- cr3 |= BIT_TWO_8BIT_SENSOR;
- cr18 |= BIT_MIPI_DATA_FORMAT_RAW12;
- break;
- case MEDIA_BUS_FMT_Y14_1X14:
- case MEDIA_BUS_FMT_SBGGR14_1X14:
- case MEDIA_BUS_FMT_SGBRG14_1X14:
- case MEDIA_BUS_FMT_SGRBG14_1X14:
- case MEDIA_BUS_FMT_SRGGB14_1X14:
- cr3 |= BIT_TWO_8BIT_SENSOR;
- cr18 |= BIT_MIPI_DATA_FORMAT_RAW14;
- break;
-
- /*
- * The CSI bridge has a 16-bit input bus. Depending on the
- * connected source, data may be transmitted with 8 or 10 bits
- * per clock sample (in bits [9:2] or [9:0] respectively) or
- * with 16 bits per clock sample (in bits [15:0]). The data is
- * then packed into a 32-bit FIFO (as shown in figure 13-11 of
- * the i.MX8MM reference manual rev. 3).
- *
- * The data packing in a 32-bit FIFO input word is controlled by
- * the CR3 TWO_8BIT_SENSOR field (also known as SENSOR_16BITS in
- * the i.MX8MM reference manual). When set to 0, data packing
- * groups four 8-bit input samples (bits [9:2]). When set to 1,
- * data packing groups two 16-bit input samples (bits [15:0]).
- *
- * The register field CR18 MIPI_DOUBLE_CMPNT also needs to be
- * configured according to the input format for YUV 4:2:2 data.
- * The field controls the gasket between the CSI-2 receiver and
- * the CSI bridge. On i.MX7 and i.MX8MM, the field must be set
- * to 1 when the CSIS outputs 16-bit samples. On i.MX8MQ, the
- * gasket ignores the MIPI_DOUBLE_CMPNT bit and YUV 4:2:2 always
- * uses 16-bit samples. Setting MIPI_DOUBLE_CMPNT in that case
- * has no effect, but doesn't cause any issue.
- */
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_YUYV8_2X8:
- cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
- break;
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_YUYV8_1X16:
- cr3 |= BIT_TWO_8BIT_SENSOR;
- cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B |
- BIT_MIPI_DOUBLE_CMPNT;
- break;
- }
- }
-
- imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
- imx7_csi_reg_write(csi, BIT_DMA_BURST_TYPE_RFF_INCR16, CSI_CSICR2);
- imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
- imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
-
- imx7_csi_reg_write(csi, (width * out_pix->height) >> 2, CSI_CSIRXCNT);
- imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(width) |
- BIT_IMAGE_HEIGHT(out_pix->height),
- CSI_CSIIMAG_PARA);
- imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA);
-}
-
-static int imx7_csi_init(struct imx7_csi *csi)
-{
- int ret;
-
- ret = clk_prepare_enable(csi->mclk);
- if (ret < 0)
- return ret;
-
- imx7_csi_configure(csi);
-
- ret = imx7_csi_dma_setup(csi);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static void imx7_csi_deinit(struct imx7_csi *csi,
- enum vb2_buffer_state return_status)
-{
- imx7_csi_dma_cleanup(csi, return_status);
- imx7_csi_init_default(csi);
- imx7_csi_dmareq_rff_disable(csi);
- clk_disable_unprepare(csi->mclk);
-}
-
-static void imx7_csi_baseaddr_switch_on_second_frame(struct imx7_csi *csi)
-{
- u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
-
- cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL |
- BIT_BASEADDR_CHG_ERR_EN;
- cr18 |= BIT_MASK_OPTION_SECOND_FRAME;
- imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
-}
-
-static void imx7_csi_enable(struct imx7_csi *csi)
-{
- /* Clear the Rx FIFO and reflash the DMA controller. */
- imx7_csi_rx_fifo_clear(csi);
- imx7_csi_dma_reflash(csi);
-
- usleep_range(2000, 3000);
-
- /* Clear and enable the interrupts. */
- imx7_csi_irq_clear(csi);
- imx7_csi_hw_enable_irq(csi);
-
- /* Enable the RxFIFO DMA and the CSI. */
- imx7_csi_dmareq_rff_enable(csi);
- imx7_csi_hw_enable(csi);
-
- if (csi->model == IMX7_CSI_IMX8MQ)
- imx7_csi_baseaddr_switch_on_second_frame(csi);
-}
-
-static void imx7_csi_disable(struct imx7_csi *csi)
-{
- imx7_csi_dma_stop(csi);
-
- imx7_csi_dmareq_rff_disable(csi);
-
- imx7_csi_hw_disable_irq(csi);
-
- imx7_csi_hw_disable(csi);
-}
-
-/* -----------------------------------------------------------------------------
- * Interrupt Handling
- */
-
-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_vb2_buf_done(struct imx7_csi *csi)
-{
- struct imx7_csi_vb2_buffer *done, *next;
- struct vb2_buffer *vb;
- dma_addr_t phys;
-
- done = csi->active_vb2_buf[csi->buf_num];
- if (done) {
- done->vbuf.field = csi->vdev_fmt.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 = imx7_csi_video_next_buf(csi);
- 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;
- u32 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;
-}
-
-/* -----------------------------------------------------------------------------
- * Format Helpers
- */
-
-#define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0}
-
-/*
- * List of supported pixel formats for the subdevs. Keep V4L2_PIX_FMT_UYVY and
- * MEDIA_BUS_FMT_UYVY8_2X8 first to match IMX7_CSI_DEF_PIX_FORMAT and
- * IMX7_CSI_DEF_MBUS_CODE.
- */
-static const struct imx7_csi_pixfmt pixel_formats[] = {
- /*** YUV formats start here ***/
- {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .codes = IMX_BUS_FMTS(
- MEDIA_BUS_FMT_UYVY8_2X8,
- MEDIA_BUS_FMT_UYVY8_1X16
- ),
- .yuv = true,
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .codes = IMX_BUS_FMTS(
- MEDIA_BUS_FMT_YUYV8_2X8,
- MEDIA_BUS_FMT_YUYV8_1X16
- ),
- .yuv = true,
- .bpp = 16,
- },
- /*** raw bayer and grayscale formats start here ***/
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8),
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8),
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8),
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8),
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR10_1X10),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG10_1X10),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG10_1X10),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB10_1X10),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR12,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR12_1X12),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG12,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG12_1X12),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG12_1X12),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB12,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB12_1X12),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR14,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR14_1X14),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG14,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG14_1X14),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG14,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG14_1X14),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB14,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB14_1X14),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_GREY,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y8_1X8),
- .bpp = 8,
- }, {
- .fourcc = V4L2_PIX_FMT_Y10,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_Y12,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12),
- .bpp = 16,
- }, {
- .fourcc = V4L2_PIX_FMT_Y14,
- .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y14_1X14),
- .bpp = 16,
- },
-};
-
-/*
- * Search in the pixel_formats[] array for an entry with the given fourcc
- * return it.
- */
-static const struct imx7_csi_pixfmt *imx7_csi_find_pixel_format(u32 fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
- const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
-
- if (fmt->fourcc == fourcc)
- return fmt;
- }
-
- return NULL;
-}
-
-/*
- * Search in the pixel_formats[] array for an entry with the given media
- * bus code and return it.
- */
-static const struct imx7_csi_pixfmt *imx7_csi_find_mbus_format(u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
- const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
- unsigned int j;
-
- if (!fmt->codes)
- continue;
-
- for (j = 0; fmt->codes[j]; j++) {
- if (code == fmt->codes[j])
- return fmt;
- }
- }
-
- return NULL;
-}
-
-/*
- * Enumerate entries in the pixel_formats[] array that match the
- * requested search criteria. Return the media-bus code that matches
- * the search criteria at the requested match index.
- *
- * @code: The returned media-bus code that matches the search criteria at
- * the requested match index.
- * @index: The requested match index.
- */
-static int imx7_csi_enum_mbus_formats(u32 *code, u32 index)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
- const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
- unsigned int j;
-
- if (!fmt->codes)
- continue;
-
- for (j = 0; fmt->codes[j]; j++) {
- if (index == 0) {
- *code = fmt->codes[j];
- return 0;
- }
-
- index--;
- }
- }
-
- return -EINVAL;
-}
-
-static int imx7_csi_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
- const struct v4l2_mbus_framefmt *mbus,
- const struct imx7_csi_pixfmt *cc)
-{
- u32 width;
- u32 stride;
-
- if (!cc) {
- cc = imx7_csi_find_mbus_format(mbus->code);
- if (!cc)
- return -EINVAL;
- }
-
- /* Round up width for minimum burst size */
- width = round_up(mbus->width, 8);
-
- /* Round up stride for IDMAC line start address alignment */
- stride = round_up((width * cc->bpp) >> 3, 8);
-
- pix->width = width;
- pix->height = mbus->height;
- pix->pixelformat = cc->fourcc;
- pix->colorspace = mbus->colorspace;
- pix->xfer_func = mbus->xfer_func;
- pix->ycbcr_enc = mbus->ycbcr_enc;
- pix->quantization = mbus->quantization;
- pix->field = mbus->field;
- pix->bytesperline = stride;
- pix->sizeimage = stride * pix->height;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Video Capture Device - IOCTLs
- */
-
-static int imx7_csi_video_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct imx7_csi *csi = video_drvdata(file);
-
- strscpy(cap->driver, IMX7_CSI_VIDEO_NAME, sizeof(cap->driver));
- strscpy(cap->card, IMX7_CSI_VIDEO_NAME, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", dev_name(csi->dev));
-
- return 0;
-}
-
-static int imx7_csi_video_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- unsigned int index = f->index;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
- const struct imx7_csi_pixfmt *fmt = &pixel_formats[i];
-
- /*
- * If a media bus code is specified, only consider formats that
- * match it.
- */
- if (f->mbus_code) {
- unsigned int j;
-
- if (!fmt->codes)
- continue;
-
- for (j = 0; fmt->codes[j]; j++) {
- if (f->mbus_code == fmt->codes[j])
- break;
- }
-
- if (!fmt->codes[j])
- continue;
- }
-
- if (index == 0) {
- f->pixelformat = fmt->fourcc;
- return 0;
- }
-
- index--;
- }
-
- return -EINVAL;
-}
-
-static int imx7_csi_video_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- const struct imx7_csi_pixfmt *cc;
-
- if (fsize->index > 0)
- return -EINVAL;
-
- cc = imx7_csi_find_pixel_format(fsize->pixel_format);
- if (!cc)
- return -EINVAL;
-
- /*
- * TODO: The constraints are hardware-specific and may depend on the
- * pixel format. This should come from the driver using
- * imx_media_capture.
- */
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = 1;
- fsize->stepwise.max_width = 65535;
- fsize->stepwise.min_height = 1;
- fsize->stepwise.max_height = 65535;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
-
- return 0;
-}
-
-static int imx7_csi_video_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct imx7_csi *csi = video_drvdata(file);
-
- f->fmt.pix = csi->vdev_fmt;
-
- return 0;
-}
-
-static const struct imx7_csi_pixfmt *
-__imx7_csi_video_try_fmt(struct v4l2_pix_format *pixfmt,
- struct v4l2_rect *compose)
-{
- struct v4l2_mbus_framefmt fmt_src;
- const struct imx7_csi_pixfmt *cc;
-
- /*
- * Find the pixel format, default to the first supported format if not
- * found.
- */
- cc = imx7_csi_find_pixel_format(pixfmt->pixelformat);
- if (!cc) {
- pixfmt->pixelformat = IMX7_CSI_DEF_PIX_FORMAT;
- cc = imx7_csi_find_pixel_format(pixfmt->pixelformat);
- }
-
- /* Allow IDMAC interweave but enforce field order from source. */
- if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) {
- switch (pixfmt->field) {
- case V4L2_FIELD_SEQ_TB:
- pixfmt->field = V4L2_FIELD_INTERLACED_TB;
- break;
- case V4L2_FIELD_SEQ_BT:
- pixfmt->field = V4L2_FIELD_INTERLACED_BT;
- break;
- default:
- break;
- }
- }
-
- v4l2_fill_mbus_format(&fmt_src, pixfmt, 0);
- imx7_csi_mbus_fmt_to_pix_fmt(pixfmt, &fmt_src, cc);
-
- if (compose) {
- compose->width = fmt_src.width;
- compose->height = fmt_src.height;
- }
-
- return cc;
-}
-
-static int imx7_csi_video_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- __imx7_csi_video_try_fmt(&f->fmt.pix, NULL);
- return 0;
-}
-
-static int imx7_csi_video_s_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct imx7_csi *csi = video_drvdata(file);
- const struct imx7_csi_pixfmt *cc;
-
- if (vb2_is_busy(&csi->q)) {
- dev_err(csi->dev, "%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- cc = __imx7_csi_video_try_fmt(&f->fmt.pix, &csi->vdev_compose);
-
- csi->vdev_cc = cc;
- csi->vdev_fmt = f->fmt.pix;
-
- return 0;
-}
-
-static int imx7_csi_video_g_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct imx7_csi *csi = 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 = csi->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 = csi->vdev_fmt.width;
- s->r.height = csi->vdev_fmt.height;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops imx7_csi_video_ioctl_ops = {
- .vidioc_querycap = imx7_csi_video_querycap,
-
- .vidioc_enum_fmt_vid_cap = imx7_csi_video_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = imx7_csi_video_enum_framesizes,
-
- .vidioc_g_fmt_vid_cap = imx7_csi_video_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = imx7_csi_video_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = imx7_csi_video_s_fmt_vid_cap,
-
- .vidioc_g_selection = imx7_csi_video_g_selection,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-};
-
-/* -----------------------------------------------------------------------------
- * Video Capture Device - Queue Operations
- */
-
-static int imx7_csi_video_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers,
- unsigned int *nplanes,
- unsigned int sizes[],
- struct device *alloc_devs[])
-{
- struct imx7_csi *csi = vb2_get_drv_priv(vq);
- struct v4l2_pix_format *pix = &csi->vdev_fmt;
- unsigned int count = *nbuffers;
-
- if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (*nplanes) {
- if (*nplanes != 1 || sizes[0] < pix->sizeimage)
- return -EINVAL;
- count += vq->num_buffers;
- }
-
- count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count);
-
- if (*nplanes)
- *nbuffers = (count < vq->num_buffers) ? 0 :
- count - vq->num_buffers;
- else
- *nbuffers = count;
-
- *nplanes = 1;
- sizes[0] = pix->sizeimage;
-
- return 0;
-}
-
-static int imx7_csi_video_buf_init(struct vb2_buffer *vb)
-{
- struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb);
-
- INIT_LIST_HEAD(&buf->list);
-
- return 0;
-}
-
-static int imx7_csi_video_buf_prepare(struct vb2_buffer *vb)
-{
- struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
- struct v4l2_pix_format *pix = &csi->vdev_fmt;
-
- if (vb2_plane_size(vb, 0) < pix->sizeimage) {
- dev_err(csi->dev,
- "data will not fit into plane (%lu < %lu)\n",
- vb2_plane_size(vb, 0), (long)pix->sizeimage);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(vb, 0, pix->sizeimage);
-
- return 0;
-}
-
-static void imx7_csi_video_buf_queue(struct vb2_buffer *vb)
-{
- struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
- struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb);
- unsigned long flags;
-
- spin_lock_irqsave(&csi->q_lock, flags);
-
- list_add_tail(&buf->list, &csi->ready_q);
-
- spin_unlock_irqrestore(&csi->q_lock, flags);
-}
-
-static int imx7_csi_video_validate_fmt(struct imx7_csi *csi)
-{
- struct v4l2_subdev_format fmt_src;
- const struct imx7_csi_pixfmt *cc;
- int ret;
-
- /* Retrieve the media bus format on the source subdev. */
- fmt_src.pad = IMX7_CSI_PAD_SRC;
- fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(&csi->sd, pad, get_fmt, NULL, &fmt_src);
- if (ret)
- return ret;
-
- /*
- * Verify that the media bus size matches the size set on the video
- * node. It is sufficient to check the compose rectangle size without
- * checking the rounded size from pix_fmt, as the rounded size is
- * derived directly from the compose rectangle size, and will thus
- * always match if the compose rectangle matches.
- */
- if (csi->vdev_compose.width != fmt_src.format.width ||
- csi->vdev_compose.height != fmt_src.format.height)
- return -EPIPE;
-
- /*
- * Verify that the media bus code is compatible with the pixel format
- * set on the video node.
- */
- cc = imx7_csi_find_mbus_format(fmt_src.format.code);
- if (!cc || csi->vdev_cc->yuv != cc->yuv)
- return -EPIPE;
-
- return 0;
-}
-
-static int imx7_csi_video_start_streaming(struct vb2_queue *vq,
- unsigned int count)
-{
- struct imx7_csi *csi = vb2_get_drv_priv(vq);
- struct imx7_csi_vb2_buffer *buf, *tmp;
- unsigned long flags;
- int ret;
-
- ret = imx7_csi_video_validate_fmt(csi);
- if (ret) {
- dev_err(csi->dev, "capture format not valid\n");
- goto err_buffers;
- }
-
- mutex_lock(&csi->mdev.graph_mutex);
-
- ret = __video_device_pipeline_start(csi->vdev, &csi->pipe);
- if (ret)
- goto err_unlock;
-
- ret = v4l2_subdev_call(&csi->sd, video, s_stream, 1);
- if (ret)
- goto err_stop;
-
- mutex_unlock(&csi->mdev.graph_mutex);
-
- return 0;
-
-err_stop:
- __video_device_pipeline_stop(csi->vdev);
-err_unlock:
- mutex_unlock(&csi->mdev.graph_mutex);
- dev_err(csi->dev, "pipeline start failed with %d\n", ret);
-err_buffers:
- spin_lock_irqsave(&csi->q_lock, flags);
- list_for_each_entry_safe(buf, tmp, &csi->ready_q, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
- }
- spin_unlock_irqrestore(&csi->q_lock, flags);
- return ret;
-}
-
-static void imx7_csi_video_stop_streaming(struct vb2_queue *vq)
-{
- struct imx7_csi *csi = vb2_get_drv_priv(vq);
- struct imx7_csi_vb2_buffer *frame;
- struct imx7_csi_vb2_buffer *tmp;
- unsigned long flags;
-
- mutex_lock(&csi->mdev.graph_mutex);
- v4l2_subdev_call(&csi->sd, video, s_stream, 0);
- __video_device_pipeline_stop(csi->vdev);
- mutex_unlock(&csi->mdev.graph_mutex);
-
- /* release all active buffers */
- spin_lock_irqsave(&csi->q_lock, flags);
- list_for_each_entry_safe(frame, tmp, &csi->ready_q, list) {
- list_del(&frame->list);
- vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
- }
- spin_unlock_irqrestore(&csi->q_lock, flags);
-}
-
-static const struct vb2_ops imx7_csi_video_qops = {
- .queue_setup = imx7_csi_video_queue_setup,
- .buf_init = imx7_csi_video_buf_init,
- .buf_prepare = imx7_csi_video_buf_prepare,
- .buf_queue = imx7_csi_video_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .start_streaming = imx7_csi_video_start_streaming,
- .stop_streaming = imx7_csi_video_stop_streaming,
-};
-
-/* -----------------------------------------------------------------------------
- * Video Capture Device - File Operations
- */
-
-static int imx7_csi_video_open(struct file *file)
-{
- struct imx7_csi *csi = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&csi->vdev_mutex))
- return -ERESTARTSYS;
-
- ret = v4l2_fh_open(file);
- if (ret) {
- dev_err(csi->dev, "v4l2_fh_open failed\n");
- goto out;
- }
-
- ret = v4l2_pipeline_pm_get(&csi->vdev->entity);
- if (ret)
- v4l2_fh_release(file);
-
-out:
- mutex_unlock(&csi->vdev_mutex);
- return ret;
-}
-
-static int imx7_csi_video_release(struct file *file)
-{
- struct imx7_csi *csi = video_drvdata(file);
- struct vb2_queue *vq = &csi->q;
-
- mutex_lock(&csi->vdev_mutex);
-
- if (file->private_data == vq->owner) {
- vb2_queue_release(vq);
- vq->owner = NULL;
- }
-
- v4l2_pipeline_pm_put(&csi->vdev->entity);
-
- v4l2_fh_release(file);
- mutex_unlock(&csi->vdev_mutex);
- return 0;
-}
-
-static const struct v4l2_file_operations imx7_csi_video_fops = {
- .owner = THIS_MODULE,
- .open = imx7_csi_video_open,
- .release = imx7_csi_video_release,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vb2_fop_mmap,
-};
-
-/* -----------------------------------------------------------------------------
- * Video Capture Device - Init & Cleanup
- */
-
-static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi)
-{
- struct imx7_csi_vb2_buffer *buf = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&csi->q_lock, flags);
-
- /* get next queued buffer */
- if (!list_empty(&csi->ready_q)) {
- buf = list_entry(csi->ready_q.next, struct imx7_csi_vb2_buffer,
- list);
- list_del(&buf->list);
- }
-
- spin_unlock_irqrestore(&csi->q_lock, flags);
-
- return buf;
-}
-
-static int imx7_csi_video_init_format(struct imx7_csi *csi)
-{
- struct v4l2_subdev_format fmt_src = {
- .pad = IMX7_CSI_PAD_SRC,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- fmt_src.format.code = IMX7_CSI_DEF_MBUS_CODE;
- fmt_src.format.width = IMX7_CSI_DEF_PIX_WIDTH;
- fmt_src.format.height = IMX7_CSI_DEF_PIX_HEIGHT;
-
- imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &fmt_src.format, NULL);
- csi->vdev_compose.width = fmt_src.format.width;
- csi->vdev_compose.height = fmt_src.format.height;
-
- csi->vdev_cc = imx7_csi_find_pixel_format(csi->vdev_fmt.pixelformat);
-
- return 0;
-}
-
-static int imx7_csi_video_register(struct imx7_csi *csi)
-{
- struct v4l2_subdev *sd = &csi->sd;
- struct v4l2_device *v4l2_dev = sd->v4l2_dev;
- struct video_device *vdev = csi->vdev;
- int ret;
-
- vdev->v4l2_dev = v4l2_dev;
-
- /* Initialize the default format and compose rectangle. */
- ret = imx7_csi_video_init_format(csi);
- if (ret < 0)
- return ret;
-
- /* Register the video device. */
- ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
- if (ret) {
- dev_err(csi->dev, "Failed to register video device\n");
- return ret;
- }
-
- dev_info(csi->dev, "Registered %s as /dev/%s\n", vdev->name,
- video_device_node_name(vdev));
-
- /* Create the link from the CSI subdev to the video device. */
- ret = media_create_pad_link(&sd->entity, IMX7_CSI_PAD_SRC,
- &vdev->entity, 0, MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret) {
- dev_err(csi->dev, "failed to create link to device node\n");
- video_unregister_device(vdev);
- return ret;
- }
-
- return 0;
-}
-
-static void imx7_csi_video_unregister(struct imx7_csi *csi)
-{
- media_entity_cleanup(&csi->vdev->entity);
- video_unregister_device(csi->vdev);
-}
-
-static int imx7_csi_video_init(struct imx7_csi *csi)
-{
- struct video_device *vdev;
- struct vb2_queue *vq;
- int ret;
-
- mutex_init(&csi->vdev_mutex);
- INIT_LIST_HEAD(&csi->ready_q);
- spin_lock_init(&csi->q_lock);
-
- /* Allocate and initialize the video device. */
- vdev = video_device_alloc();
- if (!vdev)
- return -ENOMEM;
-
- vdev->fops = &imx7_csi_video_fops;
- vdev->ioctl_ops = &imx7_csi_video_ioctl_ops;
- vdev->minor = -1;
- vdev->release = video_device_release;
- vdev->vfl_dir = VFL_DIR_RX;
- vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
- | V4L2_CAP_IO_MC;
- vdev->lock = &csi->vdev_mutex;
- vdev->queue = &csi->q;
-
- snprintf(vdev->name, sizeof(vdev->name), "%s capture", csi->sd.name);
-
- video_set_drvdata(vdev, csi);
- csi->vdev = vdev;
-
- /* Initialize the video device pad. */
- csi->vdev_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad);
- if (ret) {
- video_device_release(vdev);
- return ret;
- }
-
- /* Initialize the vb2 queue. */
- vq = &csi->q;
- vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- vq->io_modes = VB2_MMAP | VB2_DMABUF;
- vq->drv_priv = csi;
- vq->buf_struct_size = sizeof(struct imx7_csi_vb2_buffer);
- vq->ops = &imx7_csi_video_qops;
- vq->mem_ops = &vb2_dma_contig_memops;
- vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- vq->lock = &csi->vdev_mutex;
- vq->min_buffers_needed = 2;
- vq->dev = csi->dev;
-
- ret = vb2_queue_init(vq);
- if (ret) {
- dev_err(csi->dev, "vb2_queue_init failed\n");
- video_device_release(vdev);
- return ret;
- }
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 Subdev Operations
- */
-
-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) {
- ret = -EPIPE;
- goto out_unlock;
- }
-
- if (csi->is_streaming == !!enable)
- goto out_unlock;
-
- if (enable) {
- ret = imx7_csi_init(csi);
- if (ret < 0)
- goto out_unlock;
-
- ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1);
- if (ret < 0) {
- imx7_csi_deinit(csi, VB2_BUF_STATE_QUEUED);
- goto out_unlock;
- }
-
- imx7_csi_enable(csi);
- } else {
- imx7_csi_disable(csi);
-
- v4l2_subdev_call(csi->src_sd, video, s_stream, 0);
-
- imx7_csi_deinit(csi, VB2_BUF_STATE_ERROR);
- }
-
- 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_state *sd_state,
- unsigned int pad,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad);
-
- return &csi->format_mbus[pad];
-}
-
-static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state)
-{
- const enum v4l2_subdev_format_whence which =
- sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
- const struct imx7_csi_pixfmt *cc;
- int i;
-
- cc = imx7_csi_find_mbus_format(IMX7_CSI_DEF_MBUS_CODE);
-
- for (i = 0; i < IMX7_CSI_PADS_NUM; i++) {
- struct v4l2_mbus_framefmt *mf =
- imx7_csi_get_format(csi, sd_state, i, which);
-
- mf->code = IMX7_CSI_DEF_MBUS_CODE;
- mf->width = IMX7_CSI_DEF_PIX_WIDTH;
- mf->height = IMX7_CSI_DEF_PIX_HEIGHT;
- mf->field = V4L2_FIELD_NONE;
-
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- mf->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mf->colorspace);
- mf->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mf->colorspace);
- mf->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(!cc->yuv,
- mf->colorspace, mf->ycbcr_enc);
-
- csi->cc[i] = cc;
- }
-
- return 0;
-}
-
-static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- 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, sd_state, IMX7_CSI_PAD_SINK,
- code->which);
-
- switch (code->pad) {
- case IMX7_CSI_PAD_SINK:
- ret = imx7_csi_enum_mbus_formats(&code->code, code->index);
- 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_state *sd_state,
- 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, sd_state, sdformat->pad,
- sdformat->which);
- if (!fmt) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
- sdformat->format = *fmt;
-
-out_unlock:
- mutex_unlock(&csi->lock);
-
- return ret;
-}
-
-/*
- * Default the colorspace in tryfmt to SRGB if set to an unsupported
- * colorspace or not initialized. Then set the remaining colorimetry
- * parameters based on the colorspace if they are uninitialized.
- *
- * tryfmt->code must be set on entry.
- */
-static void imx7_csi_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt)
-{
- const struct imx7_csi_pixfmt *cc;
- bool is_rgb = false;
-
- cc = imx7_csi_find_mbus_format(tryfmt->code);
- if (cc && !cc->yuv)
- is_rgb = true;
-
- switch (tryfmt->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_JPEG:
- case V4L2_COLORSPACE_SRGB:
- case V4L2_COLORSPACE_BT2020:
- case V4L2_COLORSPACE_OPRGB:
- case V4L2_COLORSPACE_DCI_P3:
- case V4L2_COLORSPACE_RAW:
- break;
- default:
- tryfmt->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- }
-
- if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT)
- tryfmt->xfer_func =
- V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
-
- if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
- tryfmt->ycbcr_enc =
- V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
-
- if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT)
- tryfmt->quantization =
- V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb,
- tryfmt->colorspace,
- tryfmt->ycbcr_enc);
-}
-
-static int imx7_csi_try_fmt(struct imx7_csi *csi,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat,
- const struct imx7_csi_pixfmt **cc)
-{
- const struct imx7_csi_pixfmt *in_cc;
- struct v4l2_mbus_framefmt *in_fmt;
- u32 code;
-
- in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK,
- sdformat->which);
- if (!in_fmt)
- return -EINVAL;
-
- switch (sdformat->pad) {
- case IMX7_CSI_PAD_SRC:
- in_cc = imx7_csi_find_mbus_format(in_fmt->code);
-
- sdformat->format.width = in_fmt->width;
- sdformat->format.height = in_fmt->height;
- sdformat->format.code = in_fmt->code;
- sdformat->format.field = in_fmt->field;
- *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 = imx7_csi_find_mbus_format(sdformat->format.code);
- if (!*cc) {
- code = IMX7_CSI_DEF_MBUS_CODE;
- *cc = imx7_csi_find_mbus_format(code);
- sdformat->format.code = code;
- }
-
- if (sdformat->format.field != V4L2_FIELD_INTERLACED)
- sdformat->format.field = V4L2_FIELD_NONE;
- break;
- default:
- return -EINVAL;
- }
-
- imx7_csi_try_colorimetry(&sdformat->format);
-
- return 0;
-}
-
-static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *sdformat)
-{
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
- const struct imx7_csi_pixfmt *outcc;
- struct v4l2_mbus_framefmt *outfmt;
- const struct imx7_csi_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;
- }
-
- ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc);
- if (ret < 0)
- goto out_unlock;
-
- fmt = imx7_csi_get_format(csi, sd_state, 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, sd_state, &format, &outcc)) {
- ret = -EINVAL;
- goto out_unlock;
- }
- outfmt = imx7_csi_get_format(csi, sd_state, 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_ACTIVE)
- csi->cc[sdformat->pad] = cc;
-
-out_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 media_pad *pad = NULL;
- unsigned int i;
- int ret;
-
- if (!csi->src_sd)
- return -EPIPE;
-
- /*
- * Validate the source link, and record whether the source uses the
- * parallel input or the CSI-2 receiver.
- */
- ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt);
- if (ret)
- return ret;
-
- switch (csi->src_sd->entity.function) {
- case MEDIA_ENT_F_VID_IF_BRIDGE:
- /* The input is the CSI-2 receiver. */
- csi->is_csi2 = true;
- break;
-
- case MEDIA_ENT_F_VID_MUX:
- /* The input is the mux, check its input. */
- for (i = 0; i < csi->src_sd->entity.num_pads; i++) {
- struct media_pad *spad = &csi->src_sd->entity.pads[i];
-
- if (!(spad->flags & MEDIA_PAD_FL_SINK))
- continue;
-
- pad = media_pad_remote_pad_first(spad);
- if (pad)
- break;
- }
-
- if (!pad)
- return -ENODEV;
-
- csi->is_csi2 = pad->entity->function == MEDIA_ENT_F_VID_IF_BRIDGE;
- break;
-
- default:
- /*
- * The input is an external entity, it must use the parallel
- * bus.
- */
- csi->is_csi2 = false;
- break;
- }
-
- return 0;
-}
-
-static int imx7_csi_registered(struct v4l2_subdev *sd)
-{
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
- int ret;
-
- ret = imx7_csi_video_init(csi);
- if (ret)
- return ret;
-
- ret = imx7_csi_video_register(csi);
- if (ret)
- return ret;
-
- ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
- if (ret)
- goto err_unreg;
-
- ret = media_device_register(&csi->mdev);
- if (ret)
- goto err_unreg;
-
- return 0;
-
-err_unreg:
- imx7_csi_video_unregister(csi);
- return ret;
-}
-
-static void imx7_csi_unregistered(struct v4l2_subdev *sd)
-{
- struct imx7_csi *csi = v4l2_get_subdevdata(sd);
-
- imx7_csi_video_unregister(csi);
-}
-
-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,
-};
-
-/* -----------------------------------------------------------------------------
- * Media Entity Operations
- */
-
-static const struct media_entity_operations imx7_csi_entity_ops = {
- .link_validate = v4l2_subdev_link_validate,
- .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
-};
-
-/* -----------------------------------------------------------------------------
- * Probe & Remove
- */
-
-static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd,
- struct v4l2_async_subdev *asd)
-{
- struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier);
- struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK];
-
- csi->src_sd = sd;
-
- return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
- MEDIA_LNK_FL_IMMUTABLE);
-}
-
-static int imx7_csi_notify_complete(struct v4l2_async_notifier *notifier)
-{
- struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier);
-
- return v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
-}
-
-static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = {
- .bound = imx7_csi_notify_bound,
- .complete = imx7_csi_notify_complete,
-};
-
-static int imx7_csi_async_register(struct imx7_csi *csi)
-{
- struct v4l2_async_subdev *asd;
- struct fwnode_handle *ep;
- int ret;
-
- v4l2_async_nf_init(&csi->notifier);
-
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
- FWNODE_GRAPH_ENDPOINT_NEXT);
- if (ep) {
- asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep,
- struct v4l2_async_subdev);
-
- fwnode_handle_put(ep);
-
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- /* OK if asd already exists */
- if (ret != -EEXIST)
- return ret;
- }
- }
-
- csi->notifier.ops = &imx7_csi_notify_ops;
-
- ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static void imx7_csi_media_cleanup(struct imx7_csi *csi)
-{
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_unregister(&csi->mdev);
- media_device_cleanup(&csi->mdev);
-}
-
-static const struct media_device_ops imx7_csi_media_ops = {
- .link_notify = v4l2_pipeline_link_notify,
-};
-
-static int imx7_csi_media_dev_init(struct imx7_csi *csi)
-{
- int ret;
-
- strscpy(csi->mdev.model, "imx-media", sizeof(csi->mdev.model));
- csi->mdev.ops = &imx7_csi_media_ops;
- csi->mdev.dev = csi->dev;
-
- csi->v4l2_dev.mdev = &csi->mdev;
- strscpy(csi->v4l2_dev.name, "imx-media",
- sizeof(csi->v4l2_dev.name));
- snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info),
- "platform:%s", dev_name(csi->mdev.dev));
-
- media_device_init(&csi->mdev);
-
- ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
- if (ret < 0) {
- v4l2_err(&csi->v4l2_dev,
- "Failed to register v4l2_device: %d\n", ret);
- goto cleanup;
- }
-
- return 0;
-
-cleanup:
- media_device_cleanup(&csi->mdev);
-
- return ret;
-}
-
-static int imx7_csi_media_init(struct imx7_csi *csi)
-{
- unsigned int i;
- int ret;
-
- /* add media device */
- ret = imx7_csi_media_dev_init(csi);
- if (ret)
- return ret;
-
- 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 = csi->dev;
- csi->sd.owner = THIS_MODULE;
- csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(csi->sd.name, sizeof(csi->sd.name), "csi");
-
- 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;
-
- ret = media_entity_pads_init(&csi->sd.entity, IMX7_CSI_PADS_NUM,
- csi->pad);
- if (ret)
- goto error;
-
- ret = v4l2_device_register_subdev(&csi->v4l2_dev, &csi->sd);
- if (ret)
- goto error;
-
- return 0;
-
-error:
- imx7_csi_media_cleanup(csi);
- return ret;
-}
-
-static int imx7_csi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct imx7_csi *csi;
- int ret;
-
- csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
- if (!csi)
- return -ENOMEM;
-
- csi->dev = dev;
- platform_set_drvdata(pdev, csi);
-
- spin_lock_init(&csi->irqlock);
- mutex_init(&csi->lock);
-
- /* Acquire resources and install interrupt handler. */
- csi->mclk = devm_clk_get(&pdev->dev, "mclk");
- if (IS_ERR(csi->mclk)) {
- ret = PTR_ERR(csi->mclk);
- dev_err(dev, "Failed to get mclk: %d", ret);
- goto destroy_mutex;
- }
-
- csi->irq = platform_get_irq(pdev, 0);
- if (csi->irq < 0) {
- ret = csi->irq;
- goto destroy_mutex;
- }
-
- csi->regbase = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(csi->regbase)) {
- ret = PTR_ERR(csi->regbase);
- goto destroy_mutex;
- }
-
- csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev);
-
- 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");
- goto destroy_mutex;
- }
-
- /* Initialize all the media device infrastructure. */
- ret = imx7_csi_media_init(csi);
- if (ret)
- goto destroy_mutex;
-
- /* Set the default mbus formats. */
- ret = imx7_csi_init_cfg(&csi->sd, NULL);
- if (ret)
- goto media_cleanup;
-
- ret = imx7_csi_async_register(csi);
- if (ret)
- goto subdev_notifier_cleanup;
-
- return 0;
-
-subdev_notifier_cleanup:
- v4l2_async_nf_unregister(&csi->notifier);
- v4l2_async_nf_cleanup(&csi->notifier);
-media_cleanup:
- imx7_csi_media_cleanup(csi);
-
-destroy_mutex:
- mutex_destroy(&csi->lock);
-
- return ret;
-}
-
-static int imx7_csi_remove(struct platform_device *pdev)
-{
- struct imx7_csi *csi = platform_get_drvdata(pdev);
-
- imx7_csi_media_cleanup(csi);
-
- v4l2_async_nf_unregister(&csi->notifier);
- v4l2_async_nf_cleanup(&csi->notifier);
- v4l2_async_unregister_subdev(&csi->sd);
-
- mutex_destroy(&csi->lock);
-
- return 0;
-}
-
-static const struct of_device_id imx7_csi_of_match[] = {
- { .compatible = "fsl,imx8mq-csi", .data = (void *)IMX7_CSI_IMX8MQ },
- { .compatible = "fsl,imx7-csi", .data = (void *)IMX7_CSI_IMX7 },
- { .compatible = "fsl,imx6ul-csi", .data = (void *)IMX7_CSI_IMX7 },
- { },
-};
-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/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c
index ce13e746c15f..e530767e80a5 100644
--- a/drivers/staging/media/ipu3/ipu3-v4l2.c
+++ b/drivers/staging/media/ipu3/ipu3-v4l2.c
@@ -188,6 +188,28 @@ static int imgu_subdev_set_fmt(struct v4l2_subdev *sd,
return 0;
}
+static struct v4l2_rect *
+imgu_subdev_get_crop(struct imgu_v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(&sd->subdev, sd_state, pad);
+ else
+ return &sd->rect.eff;
+}
+
+static struct v4l2_rect *
+imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_compose(&sd->subdev, sd_state, pad);
+ else
+ return &sd->rect.bds;
+}
+
static int imgu_subdev_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
@@ -200,18 +222,12 @@ static int imgu_subdev_get_selection(struct v4l2_subdev *sd,
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
- sel->r = *v4l2_subdev_get_try_crop(sd, sd_state,
- sel->pad);
- else
- sel->r = imgu_sd->rect.eff;
+ sel->r = *imgu_subdev_get_crop(imgu_sd, sd_state, sel->pad,
+ sel->which);
return 0;
case V4L2_SEL_TGT_COMPOSE:
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
- sel->r = *v4l2_subdev_get_try_compose(sd, sd_state,
- sel->pad);
- else
- sel->r = imgu_sd->rect.bds;
+ sel->r = *imgu_subdev_get_compose(imgu_sd, sd_state, sel->pad,
+ sel->which);
return 0;
default:
return -EINVAL;
@@ -223,10 +239,9 @@ static int imgu_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_selection *sel)
{
struct imgu_device *imgu = v4l2_get_subdevdata(sd);
- struct imgu_v4l2_subdev *imgu_sd = container_of(sd,
- struct imgu_v4l2_subdev,
- subdev);
- struct v4l2_rect *rect, *try_sel;
+ struct imgu_v4l2_subdev *imgu_sd =
+ container_of(sd, struct imgu_v4l2_subdev, subdev);
+ struct v4l2_rect *rect;
dev_dbg(&imgu->pci_dev->dev,
"set subdev %u sel which %u target 0x%4x rect [%ux%u]",
@@ -238,22 +253,18 @@ static int imgu_subdev_set_selection(struct v4l2_subdev *sd,
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
- try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad);
- rect = &imgu_sd->rect.eff;
+ rect = imgu_subdev_get_crop(imgu_sd, sd_state, sel->pad,
+ sel->which);
break;
case V4L2_SEL_TGT_COMPOSE:
- try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad);
- rect = &imgu_sd->rect.bds;
+ rect = imgu_subdev_get_compose(imgu_sd, sd_state, sel->pad,
+ sel->which);
break;
default:
return -EINVAL;
}
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
- *try_sel = sel->r;
- else
- *rect = sel->r;
-
+ *rect = sel->r;
return 0;
}
diff --git a/drivers/staging/media/meson/vdec/codec_vp9.c b/drivers/staging/media/meson/vdec/codec_vp9.c
index 897f5d7a6aad..394df5761556 100644
--- a/drivers/staging/media/meson/vdec/codec_vp9.c
+++ b/drivers/staging/media/meson/vdec/codec_vp9.c
@@ -1459,7 +1459,7 @@ static void vp9_tree_merge_probs(unsigned int *prev_prob,
if (den == 0) {
new_prob = pre_prob;
} else {
- m_count = den < MODE_MV_COUNT_SAT ? den : MODE_MV_COUNT_SAT;
+ m_count = min(den, MODE_MV_COUNT_SAT);
get_prob =
clip_prob(div_r32(((int64_t)tree_left * 256 +
(den >> 1)),
@@ -1513,7 +1513,7 @@ static void adapt_coef_probs_cxt(unsigned int *prev_prob,
/* get binary prob */
num = branch_ct[node][0];
den = branch_ct[node][0] + branch_ct[node][1];
- m_count = den < count_sat ? den : count_sat;
+ m_count = min(den, count_sat);
get_prob = (den == 0) ?
128u :
@@ -1649,8 +1649,7 @@ static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc,
else if (coef_count_node_start ==
VP9_MV_BITS_1_COUNT_START)
coef_node_start = VP9_MV_BITS_1_START;
- else if (coef_count_node_start ==
- VP9_MV_CLASS0_HP_0_COUNT_START)
+ else /* node_start == VP9_MV_CLASS0_HP_0_COUNT_START */
coef_node_start = VP9_MV_CLASS0_HP_0_START;
den = count[coef_count_node_start] +
@@ -1664,8 +1663,7 @@ static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc,
if (den == 0) {
new_prob = pre_prob;
} else {
- m_count = den < MODE_MV_COUNT_SAT ?
- den : MODE_MV_COUNT_SAT;
+ m_count = min(den, MODE_MV_COUNT_SAT);
get_prob =
clip_prob(div_r32(((int64_t)
count[coef_count_node_start] * 256 +
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
index 60f3d84be828..0ad70faa9ba0 100644
--- a/drivers/staging/media/omap4iss/iss_video.c
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -19,8 +19,6 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mc.h>
-#include <asm/cacheflush.h>
-
#include "iss_video.h"
#include "iss.h"
diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h
index ca2d5edb6261..19668d28b682 100644
--- a/drivers/staging/media/omap4iss/iss_video.h
+++ b/drivers/staging/media/omap4iss/iss_video.h
@@ -53,19 +53,19 @@ enum iss_pipeline_stream_state {
enum iss_pipeline_state {
/* The stream has been started on the input video node. */
- ISS_PIPELINE_STREAM_INPUT = 1,
+ ISS_PIPELINE_STREAM_INPUT = BIT(0),
/* The stream has been started on the output video node. */
- ISS_PIPELINE_STREAM_OUTPUT = (1 << 1),
+ ISS_PIPELINE_STREAM_OUTPUT = BIT(1),
/* At least one buffer is queued on the input video node. */
- ISS_PIPELINE_QUEUE_INPUT = (1 << 2),
+ ISS_PIPELINE_QUEUE_INPUT = BIT(2),
/* At least one buffer is queued on the output video node. */
- ISS_PIPELINE_QUEUE_OUTPUT = (1 << 3),
+ ISS_PIPELINE_QUEUE_OUTPUT = BIT(3),
/* The input entity is idle, ready to be started. */
- ISS_PIPELINE_IDLE_INPUT = (1 << 4),
+ ISS_PIPELINE_IDLE_INPUT = BIT(4),
/* The output entity is idle, ready to be started. */
- ISS_PIPELINE_IDLE_OUTPUT = (1 << 5),
+ ISS_PIPELINE_IDLE_OUTPUT = BIT(5),
/* The pipeline is currently streaming. */
- ISS_PIPELINE_STREAM = (1 << 6),
+ ISS_PIPELINE_STREAM = BIT(6),
};
/*
@@ -126,9 +126,9 @@ struct iss_buffer {
enum iss_video_dmaqueue_flags {
/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
- ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+ ISS_VIDEO_DMAQUEUE_UNDERRUN = BIT(0),
/* Set when queuing buffer to an empty DMA queue */
- ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+ ISS_VIDEO_DMAQUEUE_QUEUED = BIT(1),
};
#define iss_video_dmaqueue_flags_clr(video) \
diff --git a/drivers/staging/media/rkvdec/rkvdec-vp9.c b/drivers/staging/media/rkvdec/rkvdec-vp9.c
index d8c1c0db15c7..cfae99b40ccb 100644
--- a/drivers/staging/media/rkvdec/rkvdec-vp9.c
+++ b/drivers/staging/media/rkvdec/rkvdec-vp9.c
@@ -84,6 +84,8 @@ struct rkvdec_vp9_probs {
struct rkvdec_vp9_inter_frame_probs inter;
struct rkvdec_vp9_intra_only_frame_probs intra_only;
};
+ /* 128 bit alignment */
+ u8 padding1[11];
};
/* Data structure describing auxiliary buffer format. */
@@ -1006,6 +1008,7 @@ static int rkvdec_vp9_start(struct rkvdec_ctx *ctx)
ctx->priv = vp9_ctx;
+ BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&vp9_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
diff --git a/drivers/staging/media/sunxi/Kconfig b/drivers/staging/media/sunxi/Kconfig
index 4549a135741f..62a486aba88b 100644
--- a/drivers/staging/media/sunxi/Kconfig
+++ b/drivers/staging/media/sunxi/Kconfig
@@ -12,5 +12,6 @@ config VIDEO_SUNXI
if VIDEO_SUNXI
source "drivers/staging/media/sunxi/cedrus/Kconfig"
+source "drivers/staging/media/sunxi/sun6i-isp/Kconfig"
endif
diff --git a/drivers/staging/media/sunxi/Makefile b/drivers/staging/media/sunxi/Makefile
index b87140b0e15f..3d20b2f0e644 100644
--- a/drivers/staging/media/sunxi/Makefile
+++ b/drivers/staging/media/sunxi/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus/
+obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp/
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 55c54dfdc585..a43d5ff66716 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -45,23 +45,37 @@ static int cedrus_try_ctrl(struct v4l2_ctrl *ctrl)
} else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) {
const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
struct cedrus_ctx *ctx = container_of(ctrl->handler, struct cedrus_ctx, hdl);
+ unsigned int bit_depth, max_depth;
+ struct vb2_queue *vq;
if (sps->chroma_format_idc != 1)
/* Only 4:2:0 is supported */
return -EINVAL;
- if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
- /* Luma and chroma bit depth mismatch */
+ bit_depth = max(sps->bit_depth_luma_minus8,
+ sps->bit_depth_chroma_minus8) + 8;
+
+ if (cedrus_is_capable(ctx, CEDRUS_CAPABILITY_H265_10_DEC))
+ max_depth = 10;
+ else
+ max_depth = 8;
+
+ if (bit_depth > max_depth)
return -EINVAL;
- if (ctx->dev->capabilities & CEDRUS_CAPABILITY_H265_10_DEC) {
- if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
- /* Only 8-bit and 10-bit are supported */
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ /*
+ * Bit depth can't be higher than currently set once
+ * buffers are allocated.
+ */
+ if (vb2_is_busy(vq)) {
+ if (ctx->bit_depth < bit_depth)
return -EINVAL;
} else {
- if (sps->bit_depth_luma_minus8 != 0)
- /* Only 8-bit is supported */
- return -EINVAL;
+ ctx->bit_depth = bit_depth;
+ cedrus_reset_cap_format(ctx);
}
}
@@ -77,56 +91,56 @@ static const struct cedrus_control cedrus_controls[] = {
.cfg = {
.id = V4L2_CID_STATELESS_MPEG2_SEQUENCE,
},
- .codec = CEDRUS_CODEC_MPEG2,
+ .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_MPEG2_PICTURE,
},
- .codec = CEDRUS_CODEC_MPEG2,
+ .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_MPEG2_QUANTISATION,
},
- .codec = CEDRUS_CODEC_MPEG2,
+ .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SLICE_PARAMS,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SPS,
.ops = &cedrus_ctrl_ops,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_PPS,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
@@ -134,7 +148,7 @@ static const struct cedrus_control cedrus_controls[] = {
.max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED,
.def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
@@ -142,7 +156,7 @@ static const struct cedrus_control cedrus_controls[] = {
.max = V4L2_STATELESS_H264_START_CODE_NONE,
.def = V4L2_STATELESS_H264_START_CODE_NONE,
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
/*
* We only expose supported profiles information,
@@ -160,20 +174,20 @@ static const struct cedrus_control cedrus_controls[] = {
.menu_skip_mask =
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED),
},
- .codec = CEDRUS_CODEC_H264,
+ .capabilities = CEDRUS_CAPABILITY_H264_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_SPS,
.ops = &cedrus_ctrl_ops,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_PPS,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
@@ -181,13 +195,13 @@ static const struct cedrus_control cedrus_controls[] = {
/* The driver can only handle 1 entry per slice for now */
.dims = { 1 },
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
@@ -197,7 +211,7 @@ static const struct cedrus_control cedrus_controls[] = {
.max = 0xffffffff,
.step = 1,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
@@ -205,7 +219,7 @@ static const struct cedrus_control cedrus_controls[] = {
.max = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
.def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
@@ -213,19 +227,19 @@ static const struct cedrus_control cedrus_controls[] = {
.max = V4L2_STATELESS_HEVC_START_CODE_NONE,
.def = V4L2_STATELESS_HEVC_START_CODE_NONE,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_VP8_FRAME,
},
- .codec = CEDRUS_CODEC_VP8,
+ .capabilities = CEDRUS_CAPABILITY_VP8_DEC,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
},
- .codec = CEDRUS_CODEC_H265,
+ .capabilities = CEDRUS_CAPABILITY_H265_DEC,
},
};
@@ -258,7 +272,7 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
struct v4l2_ctrl_handler *hdl = &ctx->hdl;
struct v4l2_ctrl *ctrl;
unsigned int ctrl_size;
- unsigned int i;
+ unsigned int i, j;
v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT);
if (hdl->error) {
@@ -274,7 +288,11 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
if (!ctx->ctrls)
return -ENOMEM;
+ j = 0;
for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
+ if (!cedrus_is_capable(ctx, cedrus_controls[i].capabilities))
+ continue;
+
ctrl = v4l2_ctrl_new_custom(hdl, &cedrus_controls[i].cfg,
NULL);
if (hdl->error) {
@@ -289,7 +307,7 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
return hdl->error;
}
- ctx->ctrls[i] = ctrl;
+ ctx->ctrls[j++] = ctrl;
}
ctx->fh.ctrl_handler = hdl;
@@ -350,27 +368,20 @@ static int cedrus_open(struct file *file)
v4l2_fh_init(&ctx->fh, video_devdata(file));
file->private_data = &ctx->fh;
ctx->dev = dev;
-
- ret = cedrus_init_ctrls(dev, ctx);
- if (ret)
- goto err_free;
+ ctx->bit_depth = 8;
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
&cedrus_queue_init);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
- goto err_ctrls;
+ goto err_free;
}
- ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_NV12_32L32;
- cedrus_prepare_format(&ctx->dst_fmt);
- ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE;
- /*
- * TILED_NV12 has more strict requirements, so copy the width and
- * height to src_fmt to ensure that is matches the dst_fmt resolution.
- */
- ctx->src_fmt.width = ctx->dst_fmt.width;
- ctx->src_fmt.height = ctx->dst_fmt.height;
- cedrus_prepare_format(&ctx->src_fmt);
+
+ cedrus_reset_out_format(ctx);
+
+ ret = cedrus_init_ctrls(dev, ctx);
+ if (ret)
+ goto err_m2m_release;
v4l2_fh_add(&ctx->fh);
@@ -378,8 +389,8 @@ static int cedrus_open(struct file *file)
return 0;
-err_ctrls:
- v4l2_ctrl_handler_free(&ctx->hdl);
+err_m2m_release:
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
err_free:
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
@@ -460,11 +471,6 @@ static int cedrus_probe(struct platform_device *pdev)
return ret;
}
- dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2;
- dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264;
- dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265;
- dev->dec_ops[CEDRUS_CODEC_VP8] = &cedrus_dec_ops_vp8;
-
mutex_init(&dev->dev_mutex);
INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index 93a2196006f7..522c184e2afc 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -35,14 +35,6 @@
#define CEDRUS_CAPABILITY_VP8_DEC BIT(4)
#define CEDRUS_CAPABILITY_H265_10_DEC BIT(5)
-enum cedrus_codec {
- CEDRUS_CODEC_MPEG2,
- CEDRUS_CODEC_H264,
- CEDRUS_CODEC_H265,
- CEDRUS_CODEC_VP8,
- CEDRUS_CODEC_LAST,
-};
-
enum cedrus_irq_status {
CEDRUS_IRQ_NONE,
CEDRUS_IRQ_ERROR,
@@ -57,7 +49,7 @@ enum cedrus_h264_pic_type {
struct cedrus_control {
struct v4l2_ctrl_config cfg;
- enum cedrus_codec codec;
+ unsigned int capabilities;
};
struct cedrus_h264_run {
@@ -108,7 +100,15 @@ struct cedrus_buffer {
struct {
unsigned int position;
enum cedrus_h264_pic_type pic_type;
+ void *mv_col_buf;
+ dma_addr_t mv_col_buf_dma;
+ ssize_t mv_col_buf_size;
} h264;
+ struct {
+ void *mv_col_buf;
+ dma_addr_t mv_col_buf_dma;
+ ssize_t mv_col_buf_size;
+ } h265;
} codec;
};
@@ -118,17 +118,14 @@ struct cedrus_ctx {
struct v4l2_pix_format src_fmt;
struct v4l2_pix_format dst_fmt;
- enum cedrus_codec current_codec;
+ struct cedrus_dec_ops *current_codec;
+ unsigned int bit_depth;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl **ctrls;
union {
struct {
- void *mv_col_buf;
- dma_addr_t mv_col_buf_dma;
- ssize_t mv_col_buf_field_size;
- ssize_t mv_col_buf_size;
void *pic_info_buf;
dma_addr_t pic_info_buf_dma;
ssize_t pic_info_buf_size;
@@ -142,10 +139,6 @@ struct cedrus_ctx {
ssize_t intra_pred_buf_size;
} h264;
struct {
- void *mv_col_buf;
- dma_addr_t mv_col_buf_addr;
- ssize_t mv_col_buf_size;
- ssize_t mv_col_buf_unit_size;
void *neighbor_info_buf;
dma_addr_t neighbor_info_buf_addr;
void *entry_points_buf;
@@ -170,6 +163,8 @@ struct cedrus_dec_ops {
int (*start)(struct cedrus_ctx *ctx);
void (*stop)(struct cedrus_ctx *ctx);
void (*trigger)(struct cedrus_ctx *ctx);
+ unsigned int (*extra_cap_size)(struct cedrus_ctx *ctx,
+ struct v4l2_pix_format *pix_fmt);
};
struct cedrus_variant {
@@ -185,7 +180,6 @@ struct cedrus_dev {
struct platform_device *pdev;
struct device *dev;
struct v4l2_m2m_dev *m2m_dev;
- struct cedrus_dec_ops *dec_ops[CEDRUS_CODEC_LAST];
/* Device file mutex */
struct mutex dev_mutex;
@@ -268,6 +262,12 @@ vb2_to_cedrus_buffer(const struct vb2_buffer *p)
return vb2_v4l2_to_cedrus_buffer(to_vb2_v4l2_buffer(p));
}
+static inline bool
+cedrus_is_capable(struct cedrus_ctx *ctx, unsigned int capabilities)
+{
+ return (ctx->dev->capabilities & capabilities) == capabilities;
+}
+
void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id);
u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
index e7f7602a5ab4..fbbf9e6f0f50 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
@@ -94,7 +94,7 @@ void cedrus_device_run(void *priv)
cedrus_dst_format_set(dev, &ctx->dst_fmt);
- error = dev->dec_ops[ctx->current_codec]->setup(ctx, &run);
+ error = ctx->current_codec->setup(ctx, &run);
if (error)
v4l2_err(&ctx->dev->v4l2_dev,
"Failed to setup decoding job: %d\n", error);
@@ -110,7 +110,7 @@ void cedrus_device_run(void *priv)
schedule_delayed_work(&dev->watchdog_work,
msecs_to_jiffies(2000));
- dev->dec_ops[ctx->current_codec]->trigger(ctx);
+ ctx->current_codec->trigger(ctx);
} else {
v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev,
ctx->fh.m2m_ctx,
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index a8b236cd3800..dfb401df138a 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -54,17 +54,13 @@ static void cedrus_h264_write_sram(struct cedrus_dev *dev,
cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, *buffer++);
}
-static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_ctx *ctx,
- unsigned int position,
+static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_buffer *buf,
unsigned int field)
{
- dma_addr_t addr = ctx->codec.h264.mv_col_buf_dma;
-
- /* Adjust for the position */
- addr += position * ctx->codec.h264.mv_col_buf_field_size * 2;
+ dma_addr_t addr = buf->codec.h264.mv_col_buf_dma;
/* Adjust for the field */
- addr += field * ctx->codec.h264.mv_col_buf_field_size;
+ addr += field * buf->codec.h264.mv_col_buf_size / 2;
return addr;
}
@@ -76,7 +72,6 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx,
struct cedrus_h264_sram_ref_pic *pic)
{
struct vb2_buffer *vbuf = &buf->m2m_buf.vb.vb2_buf;
- unsigned int position = buf->codec.h264.position;
pic->top_field_order_cnt = cpu_to_le32(top_field_order_cnt);
pic->bottom_field_order_cnt = cpu_to_le32(bottom_field_order_cnt);
@@ -84,14 +79,12 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx,
pic->luma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 0));
pic->chroma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 1));
- pic->mv_col_top_ptr =
- cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 0));
- pic->mv_col_bot_ptr =
- cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 1));
+ pic->mv_col_top_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 0));
+ pic->mv_col_bot_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 1));
}
-static void cedrus_write_frame_list(struct cedrus_ctx *ctx,
- struct cedrus_run *run)
+static int cedrus_write_frame_list(struct cedrus_ctx *ctx,
+ struct cedrus_run *run)
{
struct cedrus_h264_sram_ref_pic pic_list[CEDRUS_H264_FRAME_NUM];
const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params;
@@ -146,6 +139,31 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx,
output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf);
output_buf->codec.h264.position = position;
+ if (!output_buf->codec.h264.mv_col_buf_size) {
+ const struct v4l2_ctrl_h264_sps *sps = run->h264.sps;
+ unsigned int field_size;
+
+ field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) *
+ DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16;
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE))
+ field_size = field_size * 2;
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
+ field_size = field_size * 2;
+
+ output_buf->codec.h264.mv_col_buf_size = field_size * 2;
+ /* Buffer is never accessed by CPU, so we can skip kernel mapping. */
+ output_buf->codec.h264.mv_col_buf =
+ dma_alloc_attrs(dev->dev,
+ output_buf->codec.h264.mv_col_buf_size,
+ &output_buf->codec.h264.mv_col_buf_dma,
+ GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
+
+ if (!output_buf->codec.h264.mv_col_buf) {
+ output_buf->codec.h264.mv_col_buf_size = 0;
+ return -ENOMEM;
+ }
+ }
+
if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)
output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FIELD;
else if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)
@@ -162,6 +180,8 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx,
pic_list, sizeof(pic_list));
cedrus_write(dev, VE_H264_OUTPUT_FRAME_IDX, position);
+
+ return 0;
}
#define CEDRUS_MAX_REF_IDX 32
@@ -496,8 +516,9 @@ static void cedrus_h264_irq_disable(struct cedrus_ctx *ctx)
static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
{
struct cedrus_dev *dev = ctx->dev;
+ int ret;
- cedrus_engine_enable(ctx, CEDRUS_CODEC_H264);
+ cedrus_engine_enable(ctx);
cedrus_write(dev, VE_H264_SDROT_CTRL, 0);
cedrus_write(dev, VE_H264_EXTRA_BUFFER1,
@@ -506,7 +527,9 @@ static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
ctx->codec.h264.neighbor_info_buf_dma);
cedrus_write_scaling_lists(ctx, run);
- cedrus_write_frame_list(ctx, run);
+ ret = cedrus_write_frame_list(ctx, run);
+ if (ret)
+ return ret;
cedrus_set_params(ctx, run);
@@ -517,8 +540,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx)
{
struct cedrus_dev *dev = ctx->dev;
unsigned int pic_info_size;
- unsigned int field_size;
- unsigned int mv_col_size;
int ret;
/*
@@ -566,38 +587,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx)
goto err_pic_buf;
}
- field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) *
- DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16;
-
- /*
- * FIXME: This is actually conditional to
- * V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE not being set, we
- * might have to rework this if memory efficiency ever is
- * something we need to work on.
- */
- field_size = field_size * 2;
-
- /*
- * FIXME: This is actually conditional to
- * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY not being set, we might
- * have to rework this if memory efficiency ever is something
- * we need to work on.
- */
- field_size = field_size * 2;
- ctx->codec.h264.mv_col_buf_field_size = field_size;
-
- mv_col_size = field_size * 2 * CEDRUS_H264_FRAME_NUM;
- ctx->codec.h264.mv_col_buf_size = mv_col_size;
- ctx->codec.h264.mv_col_buf =
- dma_alloc_attrs(dev->dev,
- ctx->codec.h264.mv_col_buf_size,
- &ctx->codec.h264.mv_col_buf_dma,
- GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
- if (!ctx->codec.h264.mv_col_buf) {
- ret = -ENOMEM;
- goto err_neighbor_buf;
- }
-
if (ctx->src_fmt.width > 2048) {
/*
* Formulas for deblock and intra prediction buffer sizes
@@ -613,7 +602,7 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx)
GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
if (!ctx->codec.h264.deblk_buf) {
ret = -ENOMEM;
- goto err_mv_col_buf;
+ goto err_neighbor_buf;
}
/*
@@ -641,12 +630,6 @@ err_deblk_buf:
ctx->codec.h264.deblk_buf_dma,
DMA_ATTR_NO_KERNEL_MAPPING);
-err_mv_col_buf:
- dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size,
- ctx->codec.h264.mv_col_buf,
- ctx->codec.h264.mv_col_buf_dma,
- DMA_ATTR_NO_KERNEL_MAPPING);
-
err_neighbor_buf:
dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE,
ctx->codec.h264.neighbor_info_buf,
@@ -664,11 +647,26 @@ err_pic_buf:
static void cedrus_h264_stop(struct cedrus_ctx *ctx)
{
struct cedrus_dev *dev = ctx->dev;
+ struct cedrus_buffer *buf;
+ struct vb2_queue *vq;
+ unsigned int i;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ for (i = 0; i < vq->num_buffers; i++) {
+ buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i));
+
+ if (buf->codec.h264.mv_col_buf_size > 0) {
+ dma_free_attrs(dev->dev,
+ buf->codec.h264.mv_col_buf_size,
+ buf->codec.h264.mv_col_buf,
+ buf->codec.h264.mv_col_buf_dma,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+
+ buf->codec.h264.mv_col_buf_size = 0;
+ }
+ }
- dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size,
- ctx->codec.h264.mv_col_buf,
- ctx->codec.h264.mv_col_buf_dma,
- DMA_ATTR_NO_KERNEL_MAPPING);
dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE,
ctx->codec.h264.neighbor_info_buf,
ctx->codec.h264.neighbor_info_buf_dma,
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index 4952fc17f3e6..fc9297232456 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -41,6 +41,19 @@ struct cedrus_h265_sram_pred_weight {
__s8 offset;
} __packed;
+static unsigned int cedrus_h265_2bit_size(unsigned int width,
+ unsigned int height)
+{
+ /*
+ * Vendor library additionally aligns width and height to 16,
+ * but all capture formats are already aligned to that anyway,
+ * so we can skip that here. All formats are also one form of
+ * YUV 4:2:0 or another, so we can safely assume multiplication
+ * factor of 1.5.
+ */
+ return ALIGN(width / 4, 32) * height * 3 / 2;
+}
+
static enum cedrus_irq_status cedrus_h265_irq_status(struct cedrus_ctx *ctx)
{
struct cedrus_dev *dev = ctx->dev;
@@ -90,12 +103,13 @@ static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data,
}
static inline dma_addr_t
-cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx,
- unsigned int index, unsigned int field)
+cedrus_h265_frame_info_mv_col_buf_addr(struct vb2_buffer *buf,
+ unsigned int field)
{
- return ctx->codec.h265.mv_col_buf_addr + index *
- ctx->codec.h265.mv_col_buf_unit_size +
- field * ctx->codec.h265.mv_col_buf_unit_size / 2;
+ struct cedrus_buffer *cedrus_buf = vb2_to_cedrus_buffer(buf);
+
+ return cedrus_buf->codec.h265.mv_col_buf_dma +
+ field * cedrus_buf->codec.h265.mv_col_buf_size / 2;
}
static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
@@ -108,9 +122,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0);
dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1);
dma_addr_t mv_col_buf_addr[2] = {
- cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, 0),
- cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index,
- field_pic ? 1 : 0)
+ cedrus_h265_frame_info_mv_col_buf_addr(buf, 0),
+ cedrus_h265_frame_info_mv_col_buf_addr(buf, field_pic ? 1 : 0)
};
u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO +
VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index;
@@ -242,6 +255,18 @@ static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num)
}
}
+static u32 cedrus_h265_show_bits(struct cedrus_dev *dev, int num)
+{
+ cedrus_write(dev, VE_DEC_H265_TRIGGER,
+ VE_DEC_H265_TRIGGER_SHOW_BITS |
+ VE_DEC_H265_TRIGGER_TYPE_N_BITS(num));
+
+ cedrus_wait_for(dev, VE_DEC_H265_STATUS,
+ VE_DEC_H265_STATUS_VLD_BUSY);
+
+ return cedrus_read(dev, VE_DEC_H265_BITS_READ);
+}
+
static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx,
struct cedrus_run *run)
{
@@ -400,13 +425,14 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
unsigned int width_in_ctb_luma, ctb_size_luma;
unsigned int log2_max_luma_coding_block_size;
unsigned int ctb_addr_x, ctb_addr_y;
+ struct cedrus_buffer *cedrus_buf;
dma_addr_t src_buf_addr;
dma_addr_t src_buf_end_addr;
u32 chroma_log2_weight_denom;
u32 num_entry_point_offsets;
u32 output_pic_list_index;
u32 pic_order_cnt[2];
- u8 *padding;
+ u8 padding;
int count;
u32 reg;
@@ -416,6 +442,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
decode_params = run->h265.decode_params;
pred_weight_table = &slice_params->pred_weight_table;
num_entry_point_offsets = slice_params->num_entry_point_offsets;
+ cedrus_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf);
/*
* If entry points offsets are present, we should get them
@@ -433,37 +460,31 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
/* MV column buffer size and allocation. */
- if (!ctx->codec.h265.mv_col_buf_size) {
- unsigned int num_buffers =
- run->dst->vb2_buf.vb2_queue->num_buffers;
-
+ if (!cedrus_buf->codec.h265.mv_col_buf_size) {
/*
* Each CTB requires a MV col buffer with a specific unit size.
* Since the address is given with missing lsb bits, 1 KiB is
* added to each buffer to ensure proper alignment.
*/
- ctx->codec.h265.mv_col_buf_unit_size =
+ cedrus_buf->codec.h265.mv_col_buf_size =
DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) *
DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) *
CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K;
- ctx->codec.h265.mv_col_buf_size = num_buffers *
- ctx->codec.h265.mv_col_buf_unit_size;
-
/* Buffer is never accessed by CPU, so we can skip kernel mapping. */
- ctx->codec.h265.mv_col_buf =
+ cedrus_buf->codec.h265.mv_col_buf =
dma_alloc_attrs(dev->dev,
- ctx->codec.h265.mv_col_buf_size,
- &ctx->codec.h265.mv_col_buf_addr,
+ cedrus_buf->codec.h265.mv_col_buf_size,
+ &cedrus_buf->codec.h265.mv_col_buf_dma,
GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
- if (!ctx->codec.h265.mv_col_buf) {
- ctx->codec.h265.mv_col_buf_size = 0;
+ if (!cedrus_buf->codec.h265.mv_col_buf) {
+ cedrus_buf->codec.h265.mv_col_buf_size = 0;
return -ENOMEM;
}
}
/* Activate H265 engine. */
- cedrus_engine_enable(ctx, CEDRUS_CODEC_H265);
+ cedrus_engine_enable(ctx);
/* Source offset and length in bits. */
@@ -520,21 +541,22 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
if (slice_params->data_byte_offset == 0)
return -EOPNOTSUPP;
- padding = (u8 *)vb2_plane_vaddr(&run->src->vb2_buf, 0) +
- slice_params->data_byte_offset - 1;
+ cedrus_h265_skip_bits(dev, (slice_params->data_byte_offset - 1) * 8);
+
+ padding = cedrus_h265_show_bits(dev, 8);
/* at least one bit must be set in that byte */
- if (*padding == 0)
+ if (padding == 0)
return -EINVAL;
for (count = 0; count < 8; count++)
- if (*padding & (1 << count))
+ if (padding & (1 << count))
break;
/* Include the one bit. */
count++;
- cedrus_h265_skip_bits(dev, slice_params->data_byte_offset * 8 - count);
+ cedrus_h265_skip_bits(dev, 8 - count);
/* Bitstream parameters. */
@@ -793,6 +815,18 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1);
}
+ if (ctx->bit_depth > 8) {
+ unsigned int stride = ALIGN(ctx->dst_fmt.width / 4, 32);
+
+ reg = ctx->dst_fmt.sizeimage -
+ cedrus_h265_2bit_size(ctx->dst_fmt.width,
+ ctx->dst_fmt.height);
+ cedrus_write(dev, VE_DEC_H265_OFFSET_ADDR_FIRST_OUT, reg);
+
+ reg = VE_DEC_H265_10BIT_CONFIGURE_FIRST_2BIT_STRIDE(stride);
+ cedrus_write(dev, VE_DEC_H265_10BIT_CONFIGURE, reg);
+ }
+
/* Enable appropriate interruptions. */
cedrus_write(dev, VE_DEC_H265_CTRL, VE_DEC_H265_CTRL_IRQ_MASK);
@@ -803,9 +837,6 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx)
{
struct cedrus_dev *dev = ctx->dev;
- /* The buffer size is calculated at setup time. */
- ctx->codec.h265.mv_col_buf_size = 0;
-
/* Buffer is never accessed by CPU, so we can skip kernel mapping. */
ctx->codec.h265.neighbor_info_buf =
dma_alloc_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
@@ -832,14 +863,24 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx)
static void cedrus_h265_stop(struct cedrus_ctx *ctx)
{
struct cedrus_dev *dev = ctx->dev;
+ struct cedrus_buffer *buf;
+ struct vb2_queue *vq;
+ unsigned int i;
- if (ctx->codec.h265.mv_col_buf_size > 0) {
- dma_free_attrs(dev->dev, ctx->codec.h265.mv_col_buf_size,
- ctx->codec.h265.mv_col_buf,
- ctx->codec.h265.mv_col_buf_addr,
- DMA_ATTR_NO_KERNEL_MAPPING);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ for (i = 0; i < vq->num_buffers; i++) {
+ buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i));
- ctx->codec.h265.mv_col_buf_size = 0;
+ if (buf->codec.h265.mv_col_buf_size > 0) {
+ dma_free_attrs(dev->dev,
+ buf->codec.h265.mv_col_buf_size,
+ buf->codec.h265.mv_col_buf,
+ buf->codec.h265.mv_col_buf_dma,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+
+ buf->codec.h265.mv_col_buf_size = 0;
+ }
}
dma_free_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
@@ -858,6 +899,15 @@ static void cedrus_h265_trigger(struct cedrus_ctx *ctx)
cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_DEC_SLICE);
}
+static unsigned int cedrus_h265_extra_cap_size(struct cedrus_ctx *ctx,
+ struct v4l2_pix_format *pix_fmt)
+{
+ if (ctx->bit_depth > 8)
+ return cedrus_h265_2bit_size(pix_fmt->width, pix_fmt->height);
+
+ return 0;
+}
+
struct cedrus_dec_ops cedrus_dec_ops_h265 = {
.irq_clear = cedrus_h265_irq_clear,
.irq_disable = cedrus_h265_irq_disable,
@@ -866,4 +916,5 @@ struct cedrus_dec_ops cedrus_dec_ops_h265 = {
.start = cedrus_h265_start,
.stop = cedrus_h265_stop,
.trigger = cedrus_h265_trigger,
+ .extra_cap_size = cedrus_h265_extra_cap_size,
};
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index a6470a89851e..fa86a658fdc6 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -31,7 +31,7 @@
#include "cedrus_hw.h"
#include "cedrus_regs.h"
-int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec)
+int cedrus_engine_enable(struct cedrus_ctx *ctx)
{
u32 reg = 0;
@@ -42,18 +42,18 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec)
reg |= VE_MODE_REC_WR_MODE_2MB;
reg |= VE_MODE_DDR_MODE_BW_128;
- switch (codec) {
- case CEDRUS_CODEC_MPEG2:
+ switch (ctx->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_MPEG2_SLICE:
reg |= VE_MODE_DEC_MPEG;
break;
/* H.264 and VP8 both use the same decoding mode bit. */
- case CEDRUS_CODEC_H264:
- case CEDRUS_CODEC_VP8:
+ case V4L2_PIX_FMT_H264_SLICE:
+ case V4L2_PIX_FMT_VP8_FRAME:
reg |= VE_MODE_DEC_H264;
break;
- case CEDRUS_CODEC_H265:
+ case V4L2_PIX_FMT_HEVC_SLICE:
reg |= VE_MODE_DEC_H265;
break;
@@ -132,12 +132,12 @@ static irqreturn_t cedrus_irq(int irq, void *data)
return IRQ_NONE;
}
- status = dev->dec_ops[ctx->current_codec]->irq_status(ctx);
+ status = ctx->current_codec->irq_status(ctx);
if (status == CEDRUS_IRQ_NONE)
return IRQ_NONE;
- dev->dec_ops[ctx->current_codec]->irq_disable(ctx);
- dev->dec_ops[ctx->current_codec]->irq_clear(ctx);
+ ctx->current_codec->irq_disable(ctx);
+ ctx->current_codec->irq_clear(ctx);
if (status == CEDRUS_IRQ_ERROR)
state = VB2_BUF_STATE_ERROR;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
index 7c92f00e36da..6f1e701b1ea8 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h
@@ -16,7 +16,7 @@
#ifndef _CEDRUS_HW_H_
#define _CEDRUS_HW_H_
-int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec);
+int cedrus_engine_enable(struct cedrus_ctx *ctx);
void cedrus_engine_disable(struct cedrus_dev *dev);
void cedrus_dst_format_set(struct cedrus_dev *dev,
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
index c1128d2cd555..10e98f08aafc 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
@@ -66,7 +66,7 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
quantisation = run->mpeg2.quantisation;
/* Activate MPEG engine. */
- cedrus_engine_enable(ctx, CEDRUS_CODEC_MPEG2);
+ cedrus_engine_enable(ctx);
/* Set intra quantisation matrix. */
matrix = quantisation->intra_quantiser_matrix;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
index d81f7513ade0..05e6cbc548ab 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
@@ -498,6 +498,22 @@
#define VE_DEC_H265_LOW_ADDR (VE_ENGINE_DEC_H265 + 0x80)
+#define VE_DEC_H265_OFFSET_ADDR_FIRST_OUT (VE_ENGINE_DEC_H265 + 0x84)
+#define VE_DEC_H265_OFFSET_ADDR_SECOND_OUT (VE_ENGINE_DEC_H265 + 0x88)
+
+#define VE_DEC_H265_SECOND_OUT_FMT_8BIT_PLUS_2BIT 0
+#define VE_DEC_H265_SECOND_OUT_FMT_P010 1
+#define VE_DEC_H265_SECOND_OUT_FMT_10BIT_4x4_TILED 2
+
+#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_OUT_FMT(v) \
+ SHIFT_AND_MASK_BITS(v, 24, 23)
+#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_2BIT_ENABLE BIT(22)
+#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_2BIT_STRIDE(v) \
+ SHIFT_AND_MASK_BITS(v, 21, 11)
+#define VE_DEC_H265_10BIT_CONFIGURE_FIRST_2BIT_STRIDE(v) \
+ SHIFT_AND_MASK_BITS(v, 10, 0)
+#define VE_DEC_H265_10BIT_CONFIGURE (VE_ENGINE_DEC_H265 + 0x8c)
+
#define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \
SHIFT_AND_MASK_BITS(a, 31, 24)
#define VE_DEC_H265_LOW_ADDR_SECONDARY_CHROMA(a) \
@@ -505,6 +521,8 @@
#define VE_DEC_H265_LOW_ADDR_ENTRY_POINTS_BUF(a) \
SHIFT_AND_MASK_BITS(a, 7, 0)
+#define VE_DEC_H265_BITS_READ (VE_ENGINE_DEC_H265 + 0xdc)
+
#define VE_DEC_H265_SRAM_OFFSET (VE_ENGINE_DEC_H265 + 0xe0)
#define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0 0x00
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index 66714609b577..b00feaf4072c 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -56,13 +56,13 @@ static struct cedrus_format cedrus_formats[] = {
.capabilities = CEDRUS_CAPABILITY_VP8_DEC,
},
{
- .pixelformat = V4L2_PIX_FMT_NV12_32L32,
+ .pixelformat = V4L2_PIX_FMT_NV12,
.directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED,
},
{
- .pixelformat = V4L2_PIX_FMT_NV12,
+ .pixelformat = V4L2_PIX_FMT_NV12_32L32,
.directions = CEDRUS_DECODE_DST,
- .capabilities = CEDRUS_CAPABILITY_UNTILED,
},
};
@@ -73,8 +73,8 @@ static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file)
return container_of(file->private_data, struct cedrus_ctx, fh);
}
-static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions,
- unsigned int capabilities)
+static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx,
+ u32 pixelformat, u32 directions)
{
struct cedrus_format *first_valid_fmt = NULL;
struct cedrus_format *fmt;
@@ -83,7 +83,7 @@ static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions,
for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
fmt = &cedrus_formats[i];
- if ((fmt->capabilities & capabilities) != fmt->capabilities ||
+ if (!cedrus_is_capable(ctx, fmt->capabilities) ||
!(fmt->directions & directions))
continue;
@@ -177,19 +177,13 @@ static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
u32 direction)
{
struct cedrus_ctx *ctx = cedrus_file2ctx(file);
- struct cedrus_dev *dev = ctx->dev;
- unsigned int capabilities = dev->capabilities;
- struct cedrus_format *fmt;
unsigned int i, index;
/* Index among formats that match the requested direction. */
index = 0;
for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
- fmt = &cedrus_formats[i];
-
- if (fmt->capabilities && (fmt->capabilities & capabilities) !=
- fmt->capabilities)
+ if (!cedrus_is_capable(ctx, cedrus_formats[i].capabilities))
continue;
if (!(cedrus_formats[i].directions & direction))
@@ -241,15 +235,12 @@ static int cedrus_g_fmt_vid_out(struct file *file, void *priv,
return 0;
}
-static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int cedrus_try_fmt_vid_cap_p(struct cedrus_ctx *ctx,
+ struct v4l2_pix_format *pix_fmt)
{
- struct cedrus_ctx *ctx = cedrus_file2ctx(file);
- struct cedrus_dev *dev = ctx->dev;
- struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
struct cedrus_format *fmt =
- cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST,
- dev->capabilities);
+ cedrus_find_format(ctx, pix_fmt->pixelformat,
+ CEDRUS_DECODE_DST);
if (!fmt)
return -EINVAL;
@@ -259,18 +250,25 @@ static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
pix_fmt->height = ctx->src_fmt.height;
cedrus_prepare_format(pix_fmt);
+ if (ctx->current_codec->extra_cap_size)
+ pix_fmt->sizeimage +=
+ ctx->current_codec->extra_cap_size(ctx, pix_fmt);
+
return 0;
}
-static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
+static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cedrus_ctx *ctx = cedrus_file2ctx(file);
- struct cedrus_dev *dev = ctx->dev;
- struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
+ return cedrus_try_fmt_vid_cap_p(cedrus_file2ctx(file), &f->fmt.pix);
+}
+
+static int cedrus_try_fmt_vid_out_p(struct cedrus_ctx *ctx,
+ struct v4l2_pix_format *pix_fmt)
+{
struct cedrus_format *fmt =
- cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC,
- dev->capabilities);
+ cedrus_find_format(ctx, pix_fmt->pixelformat,
+ CEDRUS_DECODE_SRC);
if (!fmt)
return -EINVAL;
@@ -281,6 +279,12 @@ static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
return 0;
}
+static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return cedrus_try_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix);
+}
+
static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -301,18 +305,76 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
+void cedrus_reset_cap_format(struct cedrus_ctx *ctx)
+{
+ ctx->dst_fmt.pixelformat = 0;
+ cedrus_try_fmt_vid_cap_p(ctx, &ctx->dst_fmt);
+}
+
+static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx,
+ struct v4l2_pix_format *pix_fmt)
{
- struct cedrus_ctx *ctx = cedrus_file2ctx(file);
struct vb2_queue *vq;
- struct vb2_queue *peer_vq;
int ret;
- ret = cedrus_try_fmt_vid_out(file, priv, f);
+ ret = cedrus_try_fmt_vid_out_p(ctx, pix_fmt);
if (ret)
return ret;
+ ctx->src_fmt = *pix_fmt;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ switch (ctx->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_H264_SLICE:
+ case V4L2_PIX_FMT_HEVC_SLICE:
+ vq->subsystem_flags |=
+ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+ break;
+ default:
+ vq->subsystem_flags &=
+ ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+ break;
+ }
+
+ switch (ctx->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_MPEG2_SLICE:
+ ctx->current_codec = &cedrus_dec_ops_mpeg2;
+ break;
+ case V4L2_PIX_FMT_H264_SLICE:
+ ctx->current_codec = &cedrus_dec_ops_h264;
+ break;
+ case V4L2_PIX_FMT_HEVC_SLICE:
+ ctx->current_codec = &cedrus_dec_ops_h265;
+ break;
+ case V4L2_PIX_FMT_VP8_FRAME:
+ ctx->current_codec = &cedrus_dec_ops_vp8;
+ break;
+ }
+
+ /* Propagate format information to capture. */
+ ctx->dst_fmt.colorspace = pix_fmt->colorspace;
+ ctx->dst_fmt.xfer_func = pix_fmt->xfer_func;
+ ctx->dst_fmt.ycbcr_enc = pix_fmt->ycbcr_enc;
+ ctx->dst_fmt.quantization = pix_fmt->quantization;
+ cedrus_reset_cap_format(ctx);
+
+ return 0;
+}
+
+void cedrus_reset_out_format(struct cedrus_ctx *ctx)
+{
+ ctx->src_fmt.pixelformat = 0;
+ cedrus_s_fmt_vid_out_p(ctx, &ctx->src_fmt);
+}
+
+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;
+ struct vb2_queue *peer_vq;
+
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
/*
* In order to support dynamic resolution change,
@@ -332,34 +394,7 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
if (vb2_is_busy(peer_vq))
return -EBUSY;
- ret = cedrus_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- ctx->src_fmt = f->fmt.pix;
-
- switch (ctx->src_fmt.pixelformat) {
- case V4L2_PIX_FMT_H264_SLICE:
- case V4L2_PIX_FMT_HEVC_SLICE:
- vq->subsystem_flags |=
- VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
- break;
- default:
- vq->subsystem_flags &=
- ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
- break;
- }
-
- /* Propagate format information to capture. */
- ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
- ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
- ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
- ctx->dst_fmt.quantization = f->fmt.pix.quantization;
- ctx->dst_fmt.width = ctx->src_fmt.width;
- ctx->dst_fmt.height = ctx->src_fmt.height;
- cedrus_prepare_format(&ctx->dst_fmt);
-
- return 0;
+ return cedrus_s_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix);
}
const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
@@ -475,34 +510,13 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
struct cedrus_dev *dev = ctx->dev;
int ret = 0;
- switch (ctx->src_fmt.pixelformat) {
- case V4L2_PIX_FMT_MPEG2_SLICE:
- ctx->current_codec = CEDRUS_CODEC_MPEG2;
- break;
-
- case V4L2_PIX_FMT_H264_SLICE:
- ctx->current_codec = CEDRUS_CODEC_H264;
- break;
-
- case V4L2_PIX_FMT_HEVC_SLICE:
- ctx->current_codec = CEDRUS_CODEC_H265;
- break;
-
- case V4L2_PIX_FMT_VP8_FRAME:
- ctx->current_codec = CEDRUS_CODEC_VP8;
- break;
-
- default:
- return -EINVAL;
- }
-
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
ret = pm_runtime_resume_and_get(dev->dev);
if (ret < 0)
goto err_cleanup;
- if (dev->dec_ops[ctx->current_codec]->start) {
- ret = dev->dec_ops[ctx->current_codec]->start(ctx);
+ if (ctx->current_codec->start) {
+ ret = ctx->current_codec->start(ctx);
if (ret)
goto err_pm;
}
@@ -524,8 +538,8 @@ static void cedrus_stop_streaming(struct vb2_queue *vq)
struct cedrus_dev *dev = ctx->dev;
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
- if (dev->dec_ops[ctx->current_codec]->stop)
- dev->dec_ops[ctx->current_codec]->stop(ctx);
+ if (ctx->current_codec->stop)
+ ctx->current_codec->stop(ctx);
pm_runtime_put(dev->dev);
}
@@ -548,7 +562,7 @@ static void cedrus_buf_request_complete(struct vb2_buffer *vb)
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
}
-static struct vb2_ops cedrus_qops = {
+static const struct vb2_ops cedrus_qops = {
.queue_setup = cedrus_queue_setup,
.buf_prepare = cedrus_buf_prepare,
.buf_queue = cedrus_buf_queue,
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h
index 05050c0a0921..8e1afc16a6a1 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h
@@ -27,5 +27,7 @@ extern const struct v4l2_ioctl_ops cedrus_ioctl_ops;
int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt);
+void cedrus_reset_cap_format(struct cedrus_ctx *ctx);
+void cedrus_reset_out_format(struct cedrus_ctx *ctx);
#endif
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c
index f7714baae37d..969677a3bbf9 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c
@@ -662,7 +662,7 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
int header_size;
u32 reg;
- cedrus_engine_enable(ctx, CEDRUS_CODEC_VP8);
+ cedrus_engine_enable(ctx);
cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8);
diff --git a/drivers/staging/media/sunxi/sun6i-isp/Kconfig b/drivers/staging/media/sunxi/sun6i-isp/Kconfig
new file mode 100644
index 000000000000..68dcae9cd7d7
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SUN6I_ISP
+ tristate "Allwinner A31 Image Signal Processor (ISP) Driver"
+ depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on PM && COMMON_CLK && HAS_DMA
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_FWNODE
+ select REGMAP_MMIO
+ help
+ Support for the Allwinner A31 Image Signal Processor (ISP), also
+ found on other platforms such as the A80, A83T or V3/V3s.
diff --git a/drivers/staging/media/sunxi/sun6i-isp/Makefile b/drivers/staging/media/sunxi/sun6i-isp/Makefile
new file mode 100644
index 000000000000..da1034785144
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+sun6i-isp-y += sun6i_isp.o sun6i_isp_proc.o sun6i_isp_capture.o sun6i_isp_params.o
+
+obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp.o
diff --git a/drivers/staging/media/sunxi/sun6i-isp/TODO.txt b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt
new file mode 100644
index 000000000000..1e3236edc1ab
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt
@@ -0,0 +1,6 @@
+Unstaging requirements:
+- Add uAPI support and documentation for the configuration of all the hardware
+ modules and description of the statistics data structures;
+- Add support for statistics reporting;
+- Add userspace support in libcamera which demonstrates the ability to receive
+ statistics and adapt hardware modules configuration accordingly;
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c
new file mode 100644
index 000000000000..7b7947509b69
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
+
+#include "sun6i_isp.h"
+#include "sun6i_isp_capture.h"
+#include "sun6i_isp_params.h"
+#include "sun6i_isp_proc.h"
+#include "sun6i_isp_reg.h"
+
+/* Helpers */
+
+u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset)
+{
+ u32 *data = (u32 *)(isp_dev->tables.load.data + offset);
+
+ return *data;
+}
+
+void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset,
+ u32 value)
+{
+ u32 *data = (u32 *)(isp_dev->tables.load.data + offset);
+
+ *data = value;
+}
+
+/* State */
+
+/*
+ * The ISP works with a load buffer, which gets copied to the actual registers
+ * by the hardware before processing a frame when a specific flag is set.
+ * This is represented by tracking the ISP state in the different parts of
+ * the code with explicit sync points:
+ * - state update: to update the load buffer for the next frame if necessary;
+ * - state complete: to indicate that the state update was applied.
+ */
+
+static void sun6i_isp_state_ready(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+ u32 value;
+
+ regmap_read(regmap, SUN6I_ISP_FE_CTRL_REG, &value);
+ value |= SUN6I_ISP_FE_CTRL_PARA_READY;
+ regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, value);
+}
+
+static void sun6i_isp_state_complete(struct sun6i_isp_device *isp_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp_dev->state_lock, flags);
+
+ sun6i_isp_capture_state_complete(isp_dev);
+ sun6i_isp_params_state_complete(isp_dev);
+
+ spin_unlock_irqrestore(&isp_dev->state_lock, flags);
+}
+
+void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold)
+{
+ bool update = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp_dev->state_lock, flags);
+
+ sun6i_isp_capture_state_update(isp_dev, &update);
+ sun6i_isp_params_state_update(isp_dev, &update);
+
+ if (update && !ready_hold)
+ sun6i_isp_state_ready(isp_dev);
+
+ spin_unlock_irqrestore(&isp_dev->state_lock, flags);
+}
+
+/* Tables */
+
+static int sun6i_isp_table_setup(struct sun6i_isp_device *isp_dev,
+ struct sun6i_isp_table *table)
+{
+ table->data = dma_alloc_coherent(isp_dev->dev, table->size,
+ &table->address, GFP_KERNEL);
+ if (!table->data)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void sun6i_isp_table_cleanup(struct sun6i_isp_device *isp_dev,
+ struct sun6i_isp_table *table)
+{
+ dma_free_coherent(isp_dev->dev, table->size, table->data,
+ table->address);
+}
+
+void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+
+ regmap_write(regmap, SUN6I_ISP_REG_LOAD_ADDR_REG,
+ SUN6I_ISP_ADDR_VALUE(isp_dev->tables.load.address));
+
+ regmap_write(regmap, SUN6I_ISP_REG_SAVE_ADDR_REG,
+ SUN6I_ISP_ADDR_VALUE(isp_dev->tables.save.address));
+
+ regmap_write(regmap, SUN6I_ISP_LUT_TABLE_ADDR_REG,
+ SUN6I_ISP_ADDR_VALUE(isp_dev->tables.lut.address));
+
+ regmap_write(regmap, SUN6I_ISP_DRC_TABLE_ADDR_REG,
+ SUN6I_ISP_ADDR_VALUE(isp_dev->tables.drc.address));
+
+ regmap_write(regmap, SUN6I_ISP_STATS_ADDR_REG,
+ SUN6I_ISP_ADDR_VALUE(isp_dev->tables.stats.address));
+}
+
+static int sun6i_isp_tables_setup(struct sun6i_isp_device *isp_dev,
+ const struct sun6i_isp_variant *variant)
+{
+ struct sun6i_isp_tables *tables = &isp_dev->tables;
+ int ret;
+
+ tables->load.size = variant->table_load_save_size;
+ ret = sun6i_isp_table_setup(isp_dev, &tables->load);
+ if (ret)
+ return ret;
+
+ tables->save.size = variant->table_load_save_size;
+ ret = sun6i_isp_table_setup(isp_dev, &tables->save);
+ if (ret)
+ return ret;
+
+ tables->lut.size = variant->table_lut_size;
+ ret = sun6i_isp_table_setup(isp_dev, &tables->lut);
+ if (ret)
+ return ret;
+
+ tables->drc.size = variant->table_drc_size;
+ ret = sun6i_isp_table_setup(isp_dev, &tables->drc);
+ if (ret)
+ return ret;
+
+ tables->stats.size = variant->table_stats_size;
+ ret = sun6i_isp_table_setup(isp_dev, &tables->stats);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void sun6i_isp_tables_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_tables *tables = &isp_dev->tables;
+
+ sun6i_isp_table_cleanup(isp_dev, &tables->stats);
+ sun6i_isp_table_cleanup(isp_dev, &tables->drc);
+ sun6i_isp_table_cleanup(isp_dev, &tables->lut);
+ sun6i_isp_table_cleanup(isp_dev, &tables->save);
+ sun6i_isp_table_cleanup(isp_dev, &tables->load);
+}
+
+/* Media */
+
+static const struct media_device_ops sun6i_isp_media_ops = {
+ .link_notify = v4l2_pipeline_link_notify,
+};
+
+/* V4L2 */
+
+static int sun6i_isp_v4l2_setup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2;
+ struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
+ struct media_device *media_dev = &v4l2->media_dev;
+ struct device *dev = isp_dev->dev;
+ int ret;
+
+ /* Media Device */
+
+ strscpy(media_dev->model, SUN6I_ISP_DESCRIPTION,
+ sizeof(media_dev->model));
+ media_dev->ops = &sun6i_isp_media_ops;
+ media_dev->hw_revision = 0;
+ media_dev->dev = dev;
+
+ media_device_init(media_dev);
+
+ ret = media_device_register(media_dev);
+ if (ret) {
+ dev_err(dev, "failed to register media device\n");
+ return ret;
+ }
+
+ /* V4L2 Device */
+
+ v4l2_dev->mdev = media_dev;
+
+ ret = v4l2_device_register(dev, v4l2_dev);
+ if (ret) {
+ dev_err(dev, "failed to register v4l2 device\n");
+ goto error_media;
+ }
+
+ return 0;
+
+error_media:
+ media_device_unregister(media_dev);
+ media_device_cleanup(media_dev);
+
+ return ret;
+}
+
+static void sun6i_isp_v4l2_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2;
+
+ media_device_unregister(&v4l2->media_dev);
+ v4l2_device_unregister(&v4l2->v4l2_dev);
+ media_device_cleanup(&v4l2->media_dev);
+}
+
+/* Platform */
+
+static irqreturn_t sun6i_isp_interrupt(int irq, void *private)
+{
+ struct sun6i_isp_device *isp_dev = private;
+ struct regmap *regmap = isp_dev->regmap;
+ u32 status = 0, enable = 0;
+
+ regmap_read(regmap, SUN6I_ISP_FE_INT_STA_REG, &status);
+ regmap_read(regmap, SUN6I_ISP_FE_INT_EN_REG, &enable);
+
+ if (!status)
+ return IRQ_NONE;
+ else if (!(status & enable))
+ goto complete;
+
+ /*
+ * The ISP working cycle starts with a params-load, which makes the
+ * state from the load buffer active. Then it starts processing the
+ * frame and gives a finish interrupt. Soon after that, the next state
+ * coming from the load buffer will be applied for the next frame,
+ * giving a params-load as well.
+ *
+ * Because both frame finish and params-load are received almost
+ * at the same time (one ISR call), handle them in chronology order.
+ */
+
+ if (status & SUN6I_ISP_FE_INT_STA_FINISH)
+ sun6i_isp_capture_finish(isp_dev);
+
+ if (status & SUN6I_ISP_FE_INT_STA_PARA_LOAD) {
+ sun6i_isp_state_complete(isp_dev);
+ sun6i_isp_state_update(isp_dev, false);
+ }
+
+complete:
+ regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, status);
+
+ return IRQ_HANDLED;
+}
+
+static int sun6i_isp_suspend(struct device *dev)
+{
+ struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev);
+
+ reset_control_assert(isp_dev->reset);
+ clk_disable_unprepare(isp_dev->clock_ram);
+ clk_disable_unprepare(isp_dev->clock_mod);
+
+ return 0;
+}
+
+static int sun6i_isp_resume(struct device *dev)
+{
+ struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_deassert(isp_dev->reset);
+ if (ret) {
+ dev_err(dev, "failed to deassert reset\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(isp_dev->clock_mod);
+ if (ret) {
+ dev_err(dev, "failed to enable module clock\n");
+ goto error_reset;
+ }
+
+ ret = clk_prepare_enable(isp_dev->clock_ram);
+ if (ret) {
+ dev_err(dev, "failed to enable ram clock\n");
+ goto error_clock_mod;
+ }
+
+ return 0;
+
+error_clock_mod:
+ clk_disable_unprepare(isp_dev->clock_mod);
+
+error_reset:
+ reset_control_assert(isp_dev->reset);
+
+ return ret;
+}
+
+static const struct dev_pm_ops sun6i_isp_pm_ops = {
+ .runtime_suspend = sun6i_isp_suspend,
+ .runtime_resume = sun6i_isp_resume,
+};
+
+static const struct regmap_config sun6i_isp_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x400,
+};
+
+static int sun6i_isp_resources_setup(struct sun6i_isp_device *isp_dev,
+ struct platform_device *platform_dev)
+{
+ struct device *dev = isp_dev->dev;
+ void __iomem *io_base;
+ int irq;
+ int ret;
+
+ /* Registers */
+
+ io_base = devm_platform_ioremap_resource(platform_dev, 0);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ isp_dev->regmap = devm_regmap_init_mmio_clk(dev, "bus", io_base,
+ &sun6i_isp_regmap_config);
+ if (IS_ERR(isp_dev->regmap)) {
+ dev_err(dev, "failed to init register map\n");
+ return PTR_ERR(isp_dev->regmap);
+ }
+
+ /* Clocks */
+
+ isp_dev->clock_mod = devm_clk_get(dev, "mod");
+ if (IS_ERR(isp_dev->clock_mod)) {
+ dev_err(dev, "failed to acquire module clock\n");
+ return PTR_ERR(isp_dev->clock_mod);
+ }
+
+ isp_dev->clock_ram = devm_clk_get(dev, "ram");
+ if (IS_ERR(isp_dev->clock_ram)) {
+ dev_err(dev, "failed to acquire ram clock\n");
+ return PTR_ERR(isp_dev->clock_ram);
+ }
+
+ ret = clk_set_rate_exclusive(isp_dev->clock_mod, 297000000);
+ if (ret) {
+ dev_err(dev, "failed to set mod clock rate\n");
+ return ret;
+ }
+
+ /* Reset */
+
+ isp_dev->reset = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(isp_dev->reset)) {
+ dev_err(dev, "failed to acquire reset\n");
+ ret = PTR_ERR(isp_dev->reset);
+ goto error_clock_rate_exclusive;
+ }
+
+ /* Interrupt */
+
+ irq = platform_get_irq(platform_dev, 0);
+ if (irq < 0) {
+ dev_err(dev, "failed to get interrupt\n");
+ ret = -ENXIO;
+ goto error_clock_rate_exclusive;
+ }
+
+ ret = devm_request_irq(dev, irq, sun6i_isp_interrupt, IRQF_SHARED,
+ SUN6I_ISP_NAME, isp_dev);
+ if (ret) {
+ dev_err(dev, "failed to request interrupt\n");
+ goto error_clock_rate_exclusive;
+ }
+
+ /* Runtime PM */
+
+ pm_runtime_enable(dev);
+
+ return 0;
+
+error_clock_rate_exclusive:
+ clk_rate_exclusive_put(isp_dev->clock_mod);
+
+ return ret;
+}
+
+static void sun6i_isp_resources_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct device *dev = isp_dev->dev;
+
+ pm_runtime_disable(dev);
+ clk_rate_exclusive_put(isp_dev->clock_mod);
+}
+
+static int sun6i_isp_probe(struct platform_device *platform_dev)
+{
+ struct sun6i_isp_device *isp_dev;
+ struct device *dev = &platform_dev->dev;
+ const struct sun6i_isp_variant *variant;
+ int ret;
+
+ variant = of_device_get_match_data(dev);
+ if (!variant)
+ return -EINVAL;
+
+ isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
+ if (!isp_dev)
+ return -ENOMEM;
+
+ isp_dev->dev = dev;
+ platform_set_drvdata(platform_dev, isp_dev);
+
+ spin_lock_init(&isp_dev->state_lock);
+
+ ret = sun6i_isp_resources_setup(isp_dev, platform_dev);
+ if (ret)
+ return ret;
+
+ ret = sun6i_isp_tables_setup(isp_dev, variant);
+ if (ret) {
+ dev_err(dev, "failed to setup tables\n");
+ goto error_resources;
+ }
+
+ ret = sun6i_isp_v4l2_setup(isp_dev);
+ if (ret) {
+ dev_err(dev, "failed to setup v4l2\n");
+ goto error_tables;
+ }
+
+ ret = sun6i_isp_proc_setup(isp_dev);
+ if (ret) {
+ dev_err(dev, "failed to setup proc\n");
+ goto error_v4l2;
+ }
+
+ ret = sun6i_isp_capture_setup(isp_dev);
+ if (ret) {
+ dev_err(dev, "failed to setup capture\n");
+ goto error_proc;
+ }
+
+ ret = sun6i_isp_params_setup(isp_dev);
+ if (ret) {
+ dev_err(dev, "failed to setup params\n");
+ goto error_capture;
+ }
+
+ return 0;
+
+error_capture:
+ sun6i_isp_capture_cleanup(isp_dev);
+
+error_proc:
+ sun6i_isp_proc_cleanup(isp_dev);
+
+error_v4l2:
+ sun6i_isp_v4l2_cleanup(isp_dev);
+
+error_tables:
+ sun6i_isp_tables_cleanup(isp_dev);
+
+error_resources:
+ sun6i_isp_resources_cleanup(isp_dev);
+
+ return ret;
+}
+
+static int sun6i_isp_remove(struct platform_device *platform_dev)
+{
+ struct sun6i_isp_device *isp_dev = platform_get_drvdata(platform_dev);
+
+ sun6i_isp_params_cleanup(isp_dev);
+ sun6i_isp_capture_cleanup(isp_dev);
+ sun6i_isp_proc_cleanup(isp_dev);
+ sun6i_isp_v4l2_cleanup(isp_dev);
+ sun6i_isp_tables_cleanup(isp_dev);
+ sun6i_isp_resources_cleanup(isp_dev);
+
+ return 0;
+}
+
+/*
+ * History of sun6i-isp:
+ * - sun4i-a10-isp: initial ISP tied to the CSI0 controller,
+ * apparently unused in software implementations;
+ * - sun6i-a31-isp: separate ISP loosely based on sun4i-a10-isp,
+ * adding extra modules and features;
+ * - sun9i-a80-isp: based on sun6i-a31-isp with some register offset changes
+ * and new modules like saturation and cnr;
+ * - sun8i-a23-isp/sun8i-h3-isp: based on sun9i-a80-isp with most modules
+ * related to raw removed;
+ * - sun8i-a83t-isp: based on sun9i-a80-isp with some register offset changes
+ * - sun8i-v3s-isp: based on sun8i-a83t-isp with a new disc module;
+ */
+
+static const struct sun6i_isp_variant sun8i_v3s_isp_variant = {
+ .table_load_save_size = 0x1000,
+ .table_lut_size = 0xe00,
+ .table_drc_size = 0x600,
+ .table_stats_size = 0x2100,
+};
+
+static const struct of_device_id sun6i_isp_of_match[] = {
+ {
+ .compatible = "allwinner,sun8i-v3s-isp",
+ .data = &sun8i_v3s_isp_variant,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sun6i_isp_of_match);
+
+static struct platform_driver sun6i_isp_platform_driver = {
+ .probe = sun6i_isp_probe,
+ .remove = sun6i_isp_remove,
+ .driver = {
+ .name = SUN6I_ISP_NAME,
+ .of_match_table = of_match_ptr(sun6i_isp_of_match),
+ .pm = &sun6i_isp_pm_ops,
+ },
+};
+
+module_platform_driver(sun6i_isp_platform_driver);
+
+MODULE_DESCRIPTION("Allwinner A31 Image Signal Processor driver");
+MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h
new file mode 100644
index 000000000000..0e5f188319ff
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef _SUN6I_ISP_H_
+#define _SUN6I_ISP_H_
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun6i_isp_capture.h"
+#include "sun6i_isp_params.h"
+#include "sun6i_isp_proc.h"
+
+#define SUN6I_ISP_NAME "sun6i-isp"
+#define SUN6I_ISP_DESCRIPTION "Allwinner A31 ISP Device"
+
+enum sun6i_isp_port {
+ SUN6I_ISP_PORT_CSI0 = 0,
+ SUN6I_ISP_PORT_CSI1 = 1,
+};
+
+struct sun6i_isp_buffer {
+ struct vb2_v4l2_buffer v4l2_buffer;
+ struct list_head list;
+};
+
+struct sun6i_isp_v4l2 {
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+};
+
+struct sun6i_isp_table {
+ void *data;
+ dma_addr_t address;
+ unsigned int size;
+};
+
+struct sun6i_isp_tables {
+ struct sun6i_isp_table load;
+ struct sun6i_isp_table save;
+
+ struct sun6i_isp_table lut;
+ struct sun6i_isp_table drc;
+ struct sun6i_isp_table stats;
+};
+
+struct sun6i_isp_device {
+ struct device *dev;
+
+ struct sun6i_isp_tables tables;
+
+ struct sun6i_isp_v4l2 v4l2;
+ struct sun6i_isp_proc proc;
+ struct sun6i_isp_capture capture;
+ struct sun6i_isp_params params;
+
+ struct regmap *regmap;
+ struct clk *clock_mod;
+ struct clk *clock_ram;
+ struct reset_control *reset;
+
+ spinlock_t state_lock; /* State helpers lock. */
+};
+
+struct sun6i_isp_variant {
+ unsigned int table_load_save_size;
+ unsigned int table_lut_size;
+ unsigned int table_drc_size;
+ unsigned int table_stats_size;
+};
+
+/* Helpers */
+
+u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset);
+void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset,
+ u32 value);
+u32 sun6i_isp_address_value(dma_addr_t address);
+
+/* State */
+
+void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold);
+
+/* Tables */
+
+void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev);
+
+#endif
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c
new file mode 100644
index 000000000000..4b592820845a
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun6i_isp.h"
+#include "sun6i_isp_capture.h"
+#include "sun6i_isp_proc.h"
+#include "sun6i_isp_reg.h"
+
+/* Helpers */
+
+void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev,
+ unsigned int *width, unsigned int *height)
+{
+ if (width)
+ *width = isp_dev->capture.format.fmt.pix.width;
+ if (height)
+ *height = isp_dev->capture.format.fmt.pix.height;
+}
+
+void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev,
+ u32 *pixelformat)
+{
+ if (pixelformat)
+ *pixelformat = isp_dev->capture.format.fmt.pix.pixelformat;
+}
+
+/* Format */
+
+static const struct sun6i_isp_capture_format sun6i_isp_capture_formats[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .output_format = SUN6I_ISP_OUTPUT_FMT_YUV420SP,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .output_format = SUN6I_ISP_OUTPUT_FMT_YVU420SP,
+ },
+};
+
+const struct sun6i_isp_capture_format *
+sun6i_isp_capture_format_find(u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun6i_isp_capture_formats); i++)
+ if (sun6i_isp_capture_formats[i].pixelformat == pixelformat)
+ return &sun6i_isp_capture_formats[i];
+
+ return NULL;
+}
+
+/* Capture */
+
+static void
+sun6i_isp_capture_buffer_configure(struct sun6i_isp_device *isp_dev,
+ struct sun6i_isp_buffer *isp_buffer)
+{
+ const struct v4l2_format_info *info;
+ struct vb2_buffer *vb2_buffer;
+ unsigned int width, height;
+ unsigned int width_aligned;
+ dma_addr_t address;
+ u32 pixelformat;
+
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+ address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_Y_ADDR0_REG,
+ SUN6I_ISP_ADDR_VALUE(address));
+
+ sun6i_isp_capture_dimensions(isp_dev, &width, &height);
+ sun6i_isp_capture_format(isp_dev, &pixelformat);
+
+ info = v4l2_format_info(pixelformat);
+ if (WARN_ON(!info))
+ return;
+
+ /* Stride needs to be aligned to 4. */
+ width_aligned = ALIGN(width, 2);
+
+ if (info->comp_planes > 1) {
+ address += info->bpp[0] * width_aligned * height;
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_U_ADDR0_REG,
+ SUN6I_ISP_ADDR_VALUE(address));
+ }
+
+ if (info->comp_planes > 2) {
+ address += info->bpp[1] *
+ DIV_ROUND_UP(width_aligned, info->hdiv) *
+ DIV_ROUND_UP(height, info->vdiv);
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_V_ADDR0_REG,
+ SUN6I_ISP_ADDR_VALUE(address));
+ }
+}
+
+void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev)
+{
+ unsigned int width, height;
+ unsigned int stride_luma, stride_chroma = 0;
+ unsigned int stride_luma_div4, stride_chroma_div4;
+ const struct sun6i_isp_capture_format *format;
+ const struct v4l2_format_info *info;
+ u32 pixelformat;
+
+ sun6i_isp_capture_dimensions(isp_dev, &width, &height);
+ sun6i_isp_capture_format(isp_dev, &pixelformat);
+
+ format = sun6i_isp_capture_format_find(pixelformat);
+ if (WARN_ON(!format))
+ return;
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_SIZE_CFG_REG,
+ SUN6I_ISP_MCH_SIZE_CFG_WIDTH(width) |
+ SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(height));
+
+ info = v4l2_format_info(pixelformat);
+ if (WARN_ON(!info))
+ return;
+
+ stride_luma = width * info->bpp[0];
+ stride_luma_div4 = DIV_ROUND_UP(stride_luma, 4);
+
+ if (info->comp_planes > 1) {
+ stride_chroma = width * info->bpp[1] / info->hdiv;
+ stride_chroma_div4 = DIV_ROUND_UP(stride_chroma, 4);
+ }
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_CFG_REG,
+ SUN6I_ISP_MCH_CFG_EN |
+ SUN6I_ISP_MCH_CFG_OUTPUT_FMT(format->output_format) |
+ SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(stride_luma_div4) |
+ SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(stride_chroma_div4));
+}
+
+/* State */
+
+static void sun6i_isp_capture_state_cleanup(struct sun6i_isp_device *isp_dev,
+ bool error)
+{
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ struct sun6i_isp_buffer **isp_buffer_states[] = {
+ &state->pending, &state->current, &state->complete,
+ };
+ struct sun6i_isp_buffer *isp_buffer;
+ struct vb2_buffer *vb2_buffer;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ for (i = 0; i < ARRAY_SIZE(isp_buffer_states); i++) {
+ isp_buffer = *isp_buffer_states[i];
+ if (!isp_buffer)
+ continue;
+
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+
+ *isp_buffer_states[i] = NULL;
+ }
+
+ list_for_each_entry(isp_buffer, &state->queue, list) {
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ }
+
+ INIT_LIST_HEAD(&state->queue);
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev,
+ bool *update)
+{
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ struct sun6i_isp_buffer *isp_buffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (list_empty(&state->queue))
+ goto complete;
+
+ if (state->pending)
+ goto complete;
+
+ isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer,
+ list);
+
+ sun6i_isp_capture_buffer_configure(isp_dev, isp_buffer);
+
+ list_del(&isp_buffer->list);
+
+ state->pending = isp_buffer;
+
+ if (update)
+ *update = true;
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (!state->pending)
+ goto complete;
+
+ state->complete = state->current;
+ state->current = state->pending;
+ state->pending = NULL;
+
+ if (state->complete) {
+ struct sun6i_isp_buffer *isp_buffer = state->complete;
+ struct vb2_buffer *vb2_buffer =
+ &isp_buffer->v4l2_buffer.vb2_buf;
+
+ vb2_buffer->timestamp = ktime_get_ns();
+ isp_buffer->v4l2_buffer.sequence = state->sequence;
+
+ vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
+
+ state->complete = NULL;
+ }
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+ state->sequence++;
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+/* Queue */
+
+static int sun6i_isp_capture_queue_setup(struct vb2_queue *queue,
+ unsigned int *buffers_count,
+ unsigned int *planes_count,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage;
+
+ if (*planes_count)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *planes_count = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int sun6i_isp_capture_buffer_prepare(struct vb2_buffer *vb2_buffer)
+{
+ struct sun6i_isp_device *isp_dev =
+ vb2_get_drv_priv(vb2_buffer->vb2_queue);
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage;
+
+ if (vb2_plane_size(vb2_buffer, 0) < size) {
+ v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n",
+ vb2_plane_size(vb2_buffer, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb2_buffer, 0, size);
+
+ return 0;
+}
+
+static void sun6i_isp_capture_buffer_queue(struct vb2_buffer *vb2_buffer)
+{
+ struct sun6i_isp_device *isp_dev =
+ vb2_get_drv_priv(vb2_buffer->vb2_queue);
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer);
+ struct sun6i_isp_buffer *isp_buffer =
+ container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+ list_add_tail(&isp_buffer->list, &state->queue);
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ /* Update the state to schedule our buffer as soon as possible. */
+ if (state->streaming)
+ sun6i_isp_state_update(isp_dev, false);
+}
+
+static int sun6i_isp_capture_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ struct video_device *video_dev = &isp_dev->capture.video_dev;
+ struct v4l2_subdev *subdev = &isp_dev->proc.subdev;
+ int ret;
+
+ state->sequence = 0;
+
+ ret = video_device_pipeline_alloc_start(video_dev);
+ if (ret < 0)
+ goto error_state;
+
+ state->streaming = true;
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD)
+ goto error_streaming;
+
+ return 0;
+
+error_streaming:
+ state->streaming = false;
+
+ video_device_pipeline_stop(video_dev);
+
+error_state:
+ sun6i_isp_capture_state_cleanup(isp_dev, false);
+
+ return ret;
+}
+
+static void sun6i_isp_capture_stop_streaming(struct vb2_queue *queue)
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ struct sun6i_isp_capture_state *state = &isp_dev->capture.state;
+ struct video_device *video_dev = &isp_dev->capture.video_dev;
+ struct v4l2_subdev *subdev = &isp_dev->proc.subdev;
+
+ v4l2_subdev_call(subdev, video, s_stream, 0);
+
+ state->streaming = false;
+
+ video_device_pipeline_stop(video_dev);
+
+ sun6i_isp_capture_state_cleanup(isp_dev, true);
+}
+
+static const struct vb2_ops sun6i_isp_capture_queue_ops = {
+ .queue_setup = sun6i_isp_capture_queue_setup,
+ .buf_prepare = sun6i_isp_capture_buffer_prepare,
+ .buf_queue = sun6i_isp_capture_buffer_queue,
+ .start_streaming = sun6i_isp_capture_start_streaming,
+ .stop_streaming = sun6i_isp_capture_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/* Video Device */
+
+static void sun6i_isp_capture_format_prepare(struct v4l2_format *format)
+{
+ struct v4l2_pix_format *pix_format = &format->fmt.pix;
+ const struct v4l2_format_info *info;
+ unsigned int width, height;
+ unsigned int width_aligned;
+ unsigned int i;
+
+ v4l_bound_align_image(&pix_format->width, SUN6I_ISP_CAPTURE_WIDTH_MIN,
+ SUN6I_ISP_CAPTURE_WIDTH_MAX, 1,
+ &pix_format->height, SUN6I_ISP_CAPTURE_HEIGHT_MIN,
+ SUN6I_ISP_CAPTURE_HEIGHT_MAX, 1, 0);
+
+ if (!sun6i_isp_capture_format_find(pix_format->pixelformat))
+ pix_format->pixelformat =
+ sun6i_isp_capture_formats[0].pixelformat;
+
+ info = v4l2_format_info(pix_format->pixelformat);
+ if (WARN_ON(!info))
+ return;
+
+ width = pix_format->width;
+ height = pix_format->height;
+
+ /* Stride needs to be aligned to 4. */
+ width_aligned = ALIGN(width, 2);
+
+ pix_format->bytesperline = width_aligned * info->bpp[0];
+ pix_format->sizeimage = 0;
+
+ for (i = 0; i < info->comp_planes; i++) {
+ unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+ unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+ pix_format->sizeimage += info->bpp[i] *
+ DIV_ROUND_UP(width_aligned, hdiv) *
+ DIV_ROUND_UP(height, vdiv);
+ }
+
+ pix_format->field = V4L2_FIELD_NONE;
+
+ pix_format->colorspace = V4L2_COLORSPACE_RAW;
+ pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int sun6i_isp_capture_querycap(struct file *file, void *private,
+ struct v4l2_capability *capability)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+ struct video_device *video_dev = &isp_dev->capture.video_dev;
+
+ strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver));
+ strscpy(capability->card, video_dev->name, sizeof(capability->card));
+ snprintf(capability->bus_info, sizeof(capability->bus_info),
+ "platform:%s", dev_name(isp_dev->dev));
+
+ return 0;
+}
+
+static int sun6i_isp_capture_enum_fmt(struct file *file, void *private,
+ struct v4l2_fmtdesc *fmtdesc)
+{
+ u32 index = fmtdesc->index;
+
+ if (index >= ARRAY_SIZE(sun6i_isp_capture_formats))
+ return -EINVAL;
+
+ fmtdesc->pixelformat = sun6i_isp_capture_formats[index].pixelformat;
+
+ return 0;
+}
+
+static int sun6i_isp_capture_g_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+
+ *format = isp_dev->capture.format;
+
+ return 0;
+}
+
+static int sun6i_isp_capture_s_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+
+ if (vb2_is_busy(&isp_dev->capture.queue))
+ return -EBUSY;
+
+ sun6i_isp_capture_format_prepare(format);
+
+ isp_dev->capture.format = *format;
+
+ return 0;
+}
+
+static int sun6i_isp_capture_try_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
+{
+ sun6i_isp_capture_format_prepare(format);
+
+ return 0;
+}
+
+static int sun6i_isp_capture_enum_input(struct file *file, void *private,
+ struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ strscpy(input->name, "Camera", sizeof(input->name));
+
+ return 0;
+}
+
+static int sun6i_isp_capture_g_input(struct file *file, void *private,
+ unsigned int *index)
+{
+ *index = 0;
+
+ return 0;
+}
+
+static int sun6i_isp_capture_s_input(struct file *file, void *private,
+ unsigned int index)
+{
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops sun6i_isp_capture_ioctl_ops = {
+ .vidioc_querycap = sun6i_isp_capture_querycap,
+
+ .vidioc_enum_fmt_vid_cap = sun6i_isp_capture_enum_fmt,
+ .vidioc_g_fmt_vid_cap = sun6i_isp_capture_g_fmt,
+ .vidioc_s_fmt_vid_cap = sun6i_isp_capture_s_fmt,
+ .vidioc_try_fmt_vid_cap = sun6i_isp_capture_try_fmt,
+
+ .vidioc_enum_input = sun6i_isp_capture_enum_input,
+ .vidioc_g_input = sun6i_isp_capture_g_input,
+ .vidioc_s_input = sun6i_isp_capture_s_input,
+
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static int sun6i_isp_capture_open(struct file *file)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+ struct video_device *video_dev = &isp_dev->capture.video_dev;
+ struct mutex *lock = &isp_dev->capture.lock;
+ int ret;
+
+ if (mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_pipeline_pm_get(&video_dev->entity);
+ if (ret)
+ goto error_mutex;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ goto error_pipeline;
+
+ mutex_unlock(lock);
+
+ return 0;
+
+error_pipeline:
+ v4l2_pipeline_pm_put(&video_dev->entity);
+
+error_mutex:
+ mutex_unlock(lock);
+
+ return ret;
+}
+
+static int sun6i_isp_capture_release(struct file *file)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+ struct video_device *video_dev = &isp_dev->capture.video_dev;
+ struct mutex *lock = &isp_dev->capture.lock;
+
+ mutex_lock(lock);
+
+ _vb2_fop_release(file, NULL);
+ v4l2_pipeline_pm_put(&video_dev->entity);
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations sun6i_isp_capture_fops = {
+ .owner = THIS_MODULE,
+ .open = sun6i_isp_capture_open,
+ .release = sun6i_isp_capture_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+/* Media Entity */
+
+static int sun6i_isp_capture_link_validate(struct media_link *link)
+{
+ struct video_device *video_dev =
+ media_entity_to_video_device(link->sink->entity);
+ struct sun6i_isp_device *isp_dev = video_get_drvdata(video_dev);
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ unsigned int capture_width, capture_height;
+ unsigned int proc_width, proc_height;
+
+ sun6i_isp_capture_dimensions(isp_dev, &capture_width, &capture_height);
+ sun6i_isp_proc_dimensions(isp_dev, &proc_width, &proc_height);
+
+ /* No cropping/scaling is supported (yet). */
+ if (capture_width != proc_width || capture_height != proc_height) {
+ v4l2_err(v4l2_dev,
+ "invalid input/output dimensions: %ux%u/%ux%u\n",
+ proc_width, proc_height, capture_width,
+ capture_height);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations sun6i_isp_capture_entity_ops = {
+ .link_validate = sun6i_isp_capture_link_validate,
+};
+
+/* Capture */
+
+int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_capture *capture = &isp_dev->capture;
+ struct sun6i_isp_capture_state *state = &capture->state;
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev;
+ struct video_device *video_dev = &capture->video_dev;
+ struct vb2_queue *queue = &capture->queue;
+ struct media_pad *pad = &capture->pad;
+ struct v4l2_format *format = &capture->format;
+ struct v4l2_pix_format *pix_format = &format->fmt.pix;
+ int ret;
+
+ /* State */
+
+ INIT_LIST_HEAD(&state->queue);
+ spin_lock_init(&state->lock);
+
+ /* Media Entity */
+
+ video_dev->entity.ops = &sun6i_isp_capture_entity_ops;
+
+ /* Media Pads */
+
+ pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&video_dev->entity, 1, pad);
+ if (ret)
+ goto error_mutex;
+
+ /* Queue */
+
+ mutex_init(&capture->lock);
+
+ queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ queue->io_modes = VB2_MMAP | VB2_DMABUF;
+ queue->buf_struct_size = sizeof(struct sun6i_isp_buffer);
+ queue->ops = &sun6i_isp_capture_queue_ops;
+ queue->mem_ops = &vb2_dma_contig_memops;
+ queue->min_buffers_needed = 2;
+ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ queue->lock = &capture->lock;
+ queue->dev = isp_dev->dev;
+ queue->drv_priv = isp_dev;
+
+ ret = vb2_queue_init(queue);
+ if (ret) {
+ v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
+ goto error_media_entity;
+ }
+
+ /* V4L2 Format */
+
+ format->type = queue->type;
+ pix_format->pixelformat = sun6i_isp_capture_formats[0].pixelformat;
+ pix_format->width = 1280;
+ pix_format->height = 720;
+
+ sun6i_isp_capture_format_prepare(format);
+
+ /* Video Device */
+
+ strscpy(video_dev->name, SUN6I_ISP_CAPTURE_NAME,
+ sizeof(video_dev->name));
+ video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ video_dev->vfl_dir = VFL_DIR_RX;
+ video_dev->release = video_device_release_empty;
+ video_dev->fops = &sun6i_isp_capture_fops;
+ video_dev->ioctl_ops = &sun6i_isp_capture_ioctl_ops;
+ video_dev->v4l2_dev = v4l2_dev;
+ video_dev->queue = queue;
+ video_dev->lock = &capture->lock;
+
+ video_set_drvdata(video_dev, isp_dev);
+
+ ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ v4l2_err(v4l2_dev, "failed to register video device: %d\n",
+ ret);
+ goto error_media_entity;
+ }
+
+ /* Media Pad Link */
+
+ ret = media_create_pad_link(&proc_subdev->entity,
+ SUN6I_ISP_PROC_PAD_SOURCE,
+ &video_dev->entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
+ proc_subdev->entity.name, SUN6I_ISP_PROC_PAD_SOURCE,
+ video_dev->entity.name, 0);
+ goto error_video_device;
+ }
+
+ return 0;
+
+error_video_device:
+ vb2_video_unregister_device(video_dev);
+
+error_media_entity:
+ media_entity_cleanup(&video_dev->entity);
+
+error_mutex:
+ mutex_destroy(&capture->lock);
+
+ return ret;
+}
+
+void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_capture *capture = &isp_dev->capture;
+ struct video_device *video_dev = &capture->video_dev;
+
+ vb2_video_unregister_device(video_dev);
+ media_entity_cleanup(&video_dev->entity);
+ mutex_destroy(&capture->lock);
+}
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h
new file mode 100644
index 000000000000..0e3e4fa7a0f4
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef _SUN6I_ISP_CAPTURE_H_
+#define _SUN6I_ISP_CAPTURE_H_
+
+#include <media/v4l2-device.h>
+
+#define SUN6I_ISP_CAPTURE_NAME "sun6i-isp-capture"
+
+#define SUN6I_ISP_CAPTURE_WIDTH_MIN 16
+#define SUN6I_ISP_CAPTURE_WIDTH_MAX 3264
+#define SUN6I_ISP_CAPTURE_HEIGHT_MIN 16
+#define SUN6I_ISP_CAPTURE_HEIGHT_MAX 2448
+
+struct sun6i_isp_device;
+
+struct sun6i_isp_capture_format {
+ u32 pixelformat;
+ u8 output_format;
+};
+
+#undef current
+struct sun6i_isp_capture_state {
+ struct list_head queue;
+ spinlock_t lock; /* Queue and buffers lock. */
+
+ struct sun6i_isp_buffer *pending;
+ struct sun6i_isp_buffer *current;
+ struct sun6i_isp_buffer *complete;
+
+ unsigned int sequence;
+ bool streaming;
+};
+
+struct sun6i_isp_capture {
+ struct sun6i_isp_capture_state state;
+
+ struct video_device video_dev;
+ struct vb2_queue queue;
+ struct mutex lock; /* Queue lock. */
+ struct media_pad pad;
+
+ struct v4l2_format format;
+};
+
+/* Helpers */
+
+void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev,
+ unsigned int *width, unsigned int *height);
+void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev,
+ u32 *pixelformat);
+
+/* Format */
+
+const struct sun6i_isp_capture_format *
+sun6i_isp_capture_format_find(u32 pixelformat);
+
+/* Capture */
+
+void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev);
+
+/* State */
+
+void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev,
+ bool *update);
+void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev);
+void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev);
+
+/* Capture */
+
+int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev);
+void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev);
+
+#endif
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c
new file mode 100644
index 000000000000..8039e311cb1c
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun6i_isp.h"
+#include "sun6i_isp_params.h"
+#include "sun6i_isp_reg.h"
+#include "uapi/sun6i-isp-config.h"
+
+/* Params */
+
+static const struct sun6i_isp_params_config sun6i_isp_params_config_default = {
+ .modules_used = SUN6I_ISP_MODULE_BAYER,
+
+ .bayer = {
+ .offset_r = 32,
+ .offset_gr = 32,
+ .offset_gb = 32,
+ .offset_b = 32,
+
+ .gain_r = 256,
+ .gain_gr = 256,
+ .gain_gb = 256,
+ .gain_b = 256,
+
+ },
+
+ .bdnf = {
+ .in_dis_min = 8,
+ .in_dis_max = 16,
+
+ .coefficients_g = { 15, 4, 1 },
+ .coefficients_rb = { 15, 4 },
+ },
+};
+
+static void sun6i_isp_params_configure_ob(struct sun6i_isp_device *isp_dev)
+{
+ unsigned int width, height;
+
+ sun6i_isp_proc_dimensions(isp_dev, &width, &height);
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SIZE_REG,
+ SUN6I_ISP_OB_SIZE_WIDTH(width) |
+ SUN6I_ISP_OB_SIZE_HEIGHT(height));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_VALID_REG,
+ SUN6I_ISP_OB_VALID_WIDTH(width) |
+ SUN6I_ISP_OB_VALID_HEIGHT(height));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SRC0_VALID_START_REG,
+ SUN6I_ISP_OB_SRC0_VALID_START_HORZ(0) |
+ SUN6I_ISP_OB_SRC0_VALID_START_VERT(0));
+}
+
+static void sun6i_isp_params_configure_ae(struct sun6i_isp_device *isp_dev)
+{
+ /* These are default values that need to be set to get an output. */
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_AE_CFG_REG,
+ SUN6I_ISP_AE_CFG_LOW_BRI_TH(0xff) |
+ SUN6I_ISP_AE_CFG_HORZ_NUM(8) |
+ SUN6I_ISP_AE_CFG_HIGH_BRI_TH(0xf00) |
+ SUN6I_ISP_AE_CFG_VERT_NUM(8));
+}
+
+static void
+sun6i_isp_params_configure_bayer(struct sun6i_isp_device *isp_dev,
+ const struct sun6i_isp_params_config *config)
+{
+ const struct sun6i_isp_params_config_bayer *bayer = &config->bayer;
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET0_REG,
+ SUN6I_ISP_BAYER_OFFSET0_R(bayer->offset_r) |
+ SUN6I_ISP_BAYER_OFFSET0_GR(bayer->offset_gr));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET1_REG,
+ SUN6I_ISP_BAYER_OFFSET1_GB(bayer->offset_gb) |
+ SUN6I_ISP_BAYER_OFFSET1_B(bayer->offset_b));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN0_REG,
+ SUN6I_ISP_BAYER_GAIN0_R(bayer->gain_r) |
+ SUN6I_ISP_BAYER_GAIN0_GR(bayer->gain_gr));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN1_REG,
+ SUN6I_ISP_BAYER_GAIN1_GB(bayer->gain_gb) |
+ SUN6I_ISP_BAYER_GAIN1_B(bayer->gain_b));
+}
+
+static void sun6i_isp_params_configure_wb(struct sun6i_isp_device *isp_dev)
+{
+ /* These are default values that need to be set to get an output. */
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN0_REG,
+ SUN6I_ISP_WB_GAIN0_R(256) |
+ SUN6I_ISP_WB_GAIN0_GR(256));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN1_REG,
+ SUN6I_ISP_WB_GAIN1_GB(256) |
+ SUN6I_ISP_WB_GAIN1_B(256));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_CFG_REG,
+ SUN6I_ISP_WB_CFG_CLIP(0xfff));
+}
+
+static void sun6i_isp_params_configure_base(struct sun6i_isp_device *isp_dev)
+{
+ sun6i_isp_params_configure_ae(isp_dev);
+ sun6i_isp_params_configure_ob(isp_dev);
+ sun6i_isp_params_configure_wb(isp_dev);
+}
+
+static void
+sun6i_isp_params_configure_bdnf(struct sun6i_isp_device *isp_dev,
+ const struct sun6i_isp_params_config *config)
+{
+ const struct sun6i_isp_params_config_bdnf *bdnf = &config->bdnf;
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_CFG_REG,
+ SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(bdnf->in_dis_min) |
+ SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(bdnf->in_dis_max));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_RB_REG,
+ SUN6I_ISP_BDNF_COEF_RB(0, bdnf->coefficients_rb[0]) |
+ SUN6I_ISP_BDNF_COEF_RB(1, bdnf->coefficients_rb[1]) |
+ SUN6I_ISP_BDNF_COEF_RB(2, bdnf->coefficients_rb[2]) |
+ SUN6I_ISP_BDNF_COEF_RB(3, bdnf->coefficients_rb[3]) |
+ SUN6I_ISP_BDNF_COEF_RB(4, bdnf->coefficients_rb[4]));
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_G_REG,
+ SUN6I_ISP_BDNF_COEF_G(0, bdnf->coefficients_g[0]) |
+ SUN6I_ISP_BDNF_COEF_G(1, bdnf->coefficients_g[1]) |
+ SUN6I_ISP_BDNF_COEF_G(2, bdnf->coefficients_g[2]) |
+ SUN6I_ISP_BDNF_COEF_G(3, bdnf->coefficients_g[3]) |
+ SUN6I_ISP_BDNF_COEF_G(4, bdnf->coefficients_g[4]) |
+ SUN6I_ISP_BDNF_COEF_G(5, bdnf->coefficients_g[5]) |
+ SUN6I_ISP_BDNF_COEF_G(6, bdnf->coefficients_g[6]));
+}
+
+static void
+sun6i_isp_params_configure_modules(struct sun6i_isp_device *isp_dev,
+ const struct sun6i_isp_params_config *config)
+{
+ u32 value;
+
+ if (config->modules_used & SUN6I_ISP_MODULE_BDNF)
+ sun6i_isp_params_configure_bdnf(isp_dev, config);
+
+ if (config->modules_used & SUN6I_ISP_MODULE_BAYER)
+ sun6i_isp_params_configure_bayer(isp_dev, config);
+
+ value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG);
+ /* Clear all modules but keep input configuration. */
+ value &= SUN6I_ISP_MODULE_EN_SRC0 | SUN6I_ISP_MODULE_EN_SRC1;
+
+ if (config->modules_used & SUN6I_ISP_MODULE_BDNF)
+ value |= SUN6I_ISP_MODULE_EN_BDNF;
+
+ /* Bayer stage is always enabled. */
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value);
+}
+
+void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ sun6i_isp_params_configure_base(isp_dev);
+
+ /* Default config is only applied at the very first stream start. */
+ if (state->configured)
+ goto complete;
+
+ sun6i_isp_params_configure_modules(isp_dev,
+ &sun6i_isp_params_config_default);
+
+ state->configured = true;
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+/* State */
+
+static void sun6i_isp_params_state_cleanup(struct sun6i_isp_device *isp_dev,
+ bool error)
+{
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ struct sun6i_isp_buffer *isp_buffer;
+ struct vb2_buffer *vb2_buffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (state->pending) {
+ vb2_buffer = &state->pending->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ }
+
+ list_for_each_entry(isp_buffer, &state->queue, list) {
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ }
+
+ INIT_LIST_HEAD(&state->queue);
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev,
+ bool *update)
+{
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ struct sun6i_isp_buffer *isp_buffer;
+ struct vb2_buffer *vb2_buffer;
+ const struct sun6i_isp_params_config *config;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (list_empty(&state->queue))
+ goto complete;
+
+ if (state->pending)
+ goto complete;
+
+ isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer,
+ list);
+
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+ config = vb2_plane_vaddr(vb2_buffer, 0);
+
+ sun6i_isp_params_configure_modules(isp_dev, config);
+
+ list_del(&isp_buffer->list);
+
+ state->pending = isp_buffer;
+
+ if (update)
+ *update = true;
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ struct sun6i_isp_buffer *isp_buffer;
+ struct vb2_buffer *vb2_buffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (!state->pending)
+ goto complete;
+
+ isp_buffer = state->pending;
+ vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
+
+ vb2_buffer->timestamp = ktime_get_ns();
+
+ /* Parameters will be applied starting from the next frame. */
+ isp_buffer->v4l2_buffer.sequence = isp_dev->capture.state.sequence + 1;
+
+ vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
+
+ state->pending = NULL;
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+/* Queue */
+
+static int sun6i_isp_params_queue_setup(struct vb2_queue *queue,
+ unsigned int *buffers_count,
+ unsigned int *planes_count,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ unsigned int size = isp_dev->params.format.fmt.meta.buffersize;
+
+ if (*planes_count)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *planes_count = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int sun6i_isp_params_buffer_prepare(struct vb2_buffer *vb2_buffer)
+{
+ struct sun6i_isp_device *isp_dev =
+ vb2_get_drv_priv(vb2_buffer->vb2_queue);
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ unsigned int size = isp_dev->params.format.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb2_buffer, 0) < size) {
+ v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n",
+ vb2_plane_size(vb2_buffer, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb2_buffer, 0, size);
+
+ return 0;
+}
+
+static void sun6i_isp_params_buffer_queue(struct vb2_buffer *vb2_buffer)
+{
+ struct sun6i_isp_device *isp_dev =
+ vb2_get_drv_priv(vb2_buffer->vb2_queue);
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer);
+ struct sun6i_isp_buffer *isp_buffer =
+ container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer);
+ bool capture_streaming = isp_dev->capture.state.streaming;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+ list_add_tail(&isp_buffer->list, &state->queue);
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ if (state->streaming && capture_streaming)
+ sun6i_isp_state_update(isp_dev, false);
+}
+
+static int sun6i_isp_params_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+ bool capture_streaming = isp_dev->capture.state.streaming;
+
+ state->streaming = true;
+
+ /*
+ * Update the state as soon as possible if capture is streaming,
+ * otherwise it will be applied when capture starts streaming.
+ */
+
+ if (capture_streaming)
+ sun6i_isp_state_update(isp_dev, false);
+
+ return 0;
+}
+
+static void sun6i_isp_params_stop_streaming(struct vb2_queue *queue)
+{
+ struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
+ struct sun6i_isp_params_state *state = &isp_dev->params.state;
+
+ state->streaming = false;
+ sun6i_isp_params_state_cleanup(isp_dev, true);
+}
+
+static const struct vb2_ops sun6i_isp_params_queue_ops = {
+ .queue_setup = sun6i_isp_params_queue_setup,
+ .buf_prepare = sun6i_isp_params_buffer_prepare,
+ .buf_queue = sun6i_isp_params_buffer_queue,
+ .start_streaming = sun6i_isp_params_start_streaming,
+ .stop_streaming = sun6i_isp_params_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/* Video Device */
+
+static int sun6i_isp_params_querycap(struct file *file, void *private,
+ struct v4l2_capability *capability)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+ struct video_device *video_dev = &isp_dev->params.video_dev;
+
+ strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver));
+ strscpy(capability->card, video_dev->name, sizeof(capability->card));
+ snprintf(capability->bus_info, sizeof(capability->bus_info),
+ "platform:%s", dev_name(isp_dev->dev));
+
+ return 0;
+}
+
+static int sun6i_isp_params_enum_fmt(struct file *file, void *private,
+ struct v4l2_fmtdesc *fmtdesc)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+ struct v4l2_meta_format *params_format =
+ &isp_dev->params.format.fmt.meta;
+
+ if (fmtdesc->index > 0)
+ return -EINVAL;
+
+ fmtdesc->pixelformat = params_format->dataformat;
+
+ return 0;
+}
+
+static int sun6i_isp_params_g_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
+{
+ struct sun6i_isp_device *isp_dev = video_drvdata(file);
+
+ *format = isp_dev->params.format;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops sun6i_isp_params_ioctl_ops = {
+ .vidioc_querycap = sun6i_isp_params_querycap,
+
+ .vidioc_enum_fmt_meta_out = sun6i_isp_params_enum_fmt,
+ .vidioc_g_fmt_meta_out = sun6i_isp_params_g_fmt,
+ .vidioc_s_fmt_meta_out = sun6i_isp_params_g_fmt,
+ .vidioc_try_fmt_meta_out = sun6i_isp_params_g_fmt,
+
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct v4l2_file_operations sun6i_isp_params_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+/* Params */
+
+int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_params *params = &isp_dev->params;
+ struct sun6i_isp_params_state *state = &params->state;
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev;
+ struct video_device *video_dev = &params->video_dev;
+ struct vb2_queue *queue = &isp_dev->params.queue;
+ struct media_pad *pad = &isp_dev->params.pad;
+ struct v4l2_format *format = &isp_dev->params.format;
+ struct v4l2_meta_format *params_format = &format->fmt.meta;
+ int ret;
+
+ /* State */
+
+ INIT_LIST_HEAD(&state->queue);
+ spin_lock_init(&state->lock);
+
+ /* Media Pads */
+
+ pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&video_dev->entity, 1, pad);
+ if (ret)
+ goto error_mutex;
+
+ /* Queue */
+
+ mutex_init(&params->lock);
+
+ queue->type = V4L2_BUF_TYPE_META_OUTPUT;
+ queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ queue->buf_struct_size = sizeof(struct sun6i_isp_buffer);
+ queue->ops = &sun6i_isp_params_queue_ops;
+ queue->mem_ops = &vb2_vmalloc_memops;
+ queue->min_buffers_needed = 1;
+ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ queue->lock = &params->lock;
+ queue->dev = isp_dev->dev;
+ queue->drv_priv = isp_dev;
+
+ ret = vb2_queue_init(queue);
+ if (ret) {
+ v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
+ goto error_media_entity;
+ }
+
+ /* V4L2 Format */
+
+ format->type = queue->type;
+ params_format->dataformat = V4L2_META_FMT_SUN6I_ISP_PARAMS;
+ params_format->buffersize = sizeof(struct sun6i_isp_params_config);
+
+ /* Video Device */
+
+ strscpy(video_dev->name, SUN6I_ISP_PARAMS_NAME,
+ sizeof(video_dev->name));
+ video_dev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING;
+ video_dev->vfl_dir = VFL_DIR_TX;
+ video_dev->release = video_device_release_empty;
+ video_dev->fops = &sun6i_isp_params_fops;
+ video_dev->ioctl_ops = &sun6i_isp_params_ioctl_ops;
+ video_dev->v4l2_dev = v4l2_dev;
+ video_dev->queue = queue;
+ video_dev->lock = &params->lock;
+
+ video_set_drvdata(video_dev, isp_dev);
+
+ ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ v4l2_err(v4l2_dev, "failed to register video device: %d\n",
+ ret);
+ goto error_media_entity;
+ }
+
+ /* Media Pad Link */
+
+ ret = media_create_pad_link(&video_dev->entity, 0,
+ &proc_subdev->entity,
+ SUN6I_ISP_PROC_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
+ video_dev->entity.name, 0, proc_subdev->entity.name,
+ SUN6I_ISP_PROC_PAD_SINK_PARAMS);
+ goto error_video_device;
+ }
+
+ return 0;
+
+error_video_device:
+ vb2_video_unregister_device(video_dev);
+
+error_media_entity:
+ media_entity_cleanup(&video_dev->entity);
+
+error_mutex:
+ mutex_destroy(&params->lock);
+
+ return ret;
+}
+
+void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct sun6i_isp_params *params = &isp_dev->params;
+ struct video_device *video_dev = &params->video_dev;
+
+ vb2_video_unregister_device(video_dev);
+ media_entity_cleanup(&video_dev->entity);
+ mutex_destroy(&params->lock);
+}
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h
new file mode 100644
index 000000000000..50f10f879c42
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef _SUN6I_ISP_PARAMS_H_
+#define _SUN6I_ISP_PARAMS_H_
+
+#include <media/v4l2-device.h>
+
+#define SUN6I_ISP_PARAMS_NAME "sun6i-isp-params"
+
+struct sun6i_isp_device;
+
+struct sun6i_isp_params_state {
+ struct list_head queue; /* Queue and buffers lock. */
+ spinlock_t lock;
+
+ struct sun6i_isp_buffer *pending;
+
+ bool configured;
+ bool streaming;
+};
+
+struct sun6i_isp_params {
+ struct sun6i_isp_params_state state;
+
+ struct video_device video_dev;
+ struct vb2_queue queue;
+ struct mutex lock; /* Queue lock. */
+ struct media_pad pad;
+
+ struct v4l2_format format;
+};
+
+/* Params */
+
+void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev);
+
+/* State */
+
+void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev,
+ bool *update);
+void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev);
+
+/* Params */
+
+int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev);
+void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev);
+
+#endif
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
new file mode 100644
index 000000000000..d69d2be0add2
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun6i_isp.h"
+#include "sun6i_isp_capture.h"
+#include "sun6i_isp_params.h"
+#include "sun6i_isp_proc.h"
+#include "sun6i_isp_reg.h"
+
+/* Helpers */
+
+void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev,
+ unsigned int *width, unsigned int *height)
+{
+ if (width)
+ *width = isp_dev->proc.mbus_format.width;
+ if (height)
+ *height = isp_dev->proc.mbus_format.height;
+}
+
+/* Format */
+
+static const struct sun6i_isp_proc_format sun6i_isp_proc_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB,
+ },
+
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB,
+ },
+};
+
+const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun6i_isp_proc_formats); i++)
+ if (sun6i_isp_proc_formats[i].mbus_code == mbus_code)
+ return &sun6i_isp_proc_formats[i];
+
+ return NULL;
+}
+
+/* Processor */
+
+static void sun6i_isp_proc_irq_enable(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+
+ regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG,
+ SUN6I_ISP_FE_INT_EN_FINISH |
+ SUN6I_ISP_FE_INT_EN_START |
+ SUN6I_ISP_FE_INT_EN_PARA_SAVE |
+ SUN6I_ISP_FE_INT_EN_PARA_LOAD |
+ SUN6I_ISP_FE_INT_EN_SRC0_FIFO |
+ SUN6I_ISP_FE_INT_EN_ROT_FINISH);
+}
+
+static void sun6i_isp_proc_irq_disable(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+
+ regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0);
+}
+
+static void sun6i_isp_proc_irq_clear(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+
+ regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0);
+ regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG,
+ SUN6I_ISP_FE_INT_STA_CLEAR);
+}
+
+static void sun6i_isp_proc_enable(struct sun6i_isp_device *isp_dev,
+ struct sun6i_isp_proc_source *source)
+{
+ struct sun6i_isp_proc *proc = &isp_dev->proc;
+ struct regmap *regmap = isp_dev->regmap;
+ u8 mode;
+
+ /* Frontend */
+
+ if (source == &proc->source_csi0)
+ mode = SUN6I_ISP_SRC_MODE_CSI(0);
+ else
+ mode = SUN6I_ISP_SRC_MODE_CSI(1);
+
+ regmap_write(regmap, SUN6I_ISP_FE_CFG_REG,
+ SUN6I_ISP_FE_CFG_EN | SUN6I_ISP_FE_CFG_SRC0_MODE(mode));
+
+ regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG,
+ SUN6I_ISP_FE_CTRL_VCAP_EN | SUN6I_ISP_FE_CTRL_PARA_READY);
+}
+
+static void sun6i_isp_proc_disable(struct sun6i_isp_device *isp_dev)
+{
+ struct regmap *regmap = isp_dev->regmap;
+
+ /* Frontend */
+
+ regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, 0);
+ regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, 0);
+}
+
+static void sun6i_isp_proc_configure(struct sun6i_isp_device *isp_dev)
+{
+ struct v4l2_mbus_framefmt *mbus_format = &isp_dev->proc.mbus_format;
+ const struct sun6i_isp_proc_format *format;
+ u32 value;
+
+ /* Module */
+
+ value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG);
+ value |= SUN6I_ISP_MODULE_EN_SRC0;
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value);
+
+ /* Input */
+
+ format = sun6i_isp_proc_format_find(mbus_format->code);
+ if (WARN_ON(!format))
+ return;
+
+ sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODE_REG,
+ SUN6I_ISP_MODE_INPUT_FMT(format->input_format) |
+ SUN6I_ISP_MODE_INPUT_YUV_SEQ(format->input_yuv_seq) |
+ SUN6I_ISP_MODE_SHARP(1) |
+ SUN6I_ISP_MODE_HIST(2));
+}
+
+/* V4L2 Subdev */
+
+static int sun6i_isp_proc_s_stream(struct v4l2_subdev *subdev, int on)
+{
+ struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev);
+ struct sun6i_isp_proc *proc = &isp_dev->proc;
+ struct media_pad *local_pad = &proc->pads[SUN6I_ISP_PROC_PAD_SINK_CSI];
+ struct device *dev = isp_dev->dev;
+ struct sun6i_isp_proc_source *source;
+ struct v4l2_subdev *source_subdev;
+ struct media_pad *remote_pad;
+ /* Initialize to 0 to use both in disable label (ret != 0) and off. */
+ int ret = 0;
+
+ /* Source */
+
+ remote_pad = media_pad_remote_pad_unique(local_pad);
+ if (IS_ERR(remote_pad)) {
+ dev_err(dev,
+ "zero or more than a single source connected to the bridge\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ if (source_subdev == proc->source_csi0.subdev)
+ source = &proc->source_csi0;
+ else
+ source = &proc->source_csi1;
+
+ if (!on) {
+ sun6i_isp_proc_irq_disable(isp_dev);
+ v4l2_subdev_call(source_subdev, video, s_stream, 0);
+ goto disable;
+ }
+
+ /* PM */
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ /* Clear */
+
+ sun6i_isp_proc_irq_clear(isp_dev);
+
+ /* Configure */
+
+ sun6i_isp_tables_configure(isp_dev);
+ sun6i_isp_params_configure(isp_dev);
+ sun6i_isp_proc_configure(isp_dev);
+ sun6i_isp_capture_configure(isp_dev);
+
+ /* State Update */
+
+ sun6i_isp_state_update(isp_dev, true);
+
+ /* Enable */
+
+ sun6i_isp_proc_irq_enable(isp_dev);
+ sun6i_isp_proc_enable(isp_dev, source);
+
+ ret = v4l2_subdev_call(source_subdev, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ sun6i_isp_proc_irq_disable(isp_dev);
+ goto disable;
+ }
+
+ return 0;
+
+disable:
+ sun6i_isp_proc_disable(isp_dev);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops sun6i_isp_proc_video_ops = {
+ .s_stream = sun6i_isp_proc_s_stream,
+};
+
+static void
+sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
+{
+ if (!sun6i_isp_proc_format_find(mbus_format->code))
+ mbus_format->code = sun6i_isp_proc_formats[0].mbus_code;
+
+ mbus_format->field = V4L2_FIELD_NONE;
+ mbus_format->colorspace = V4L2_COLORSPACE_RAW;
+ mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state)
+{
+ struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev);
+ unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI;
+ struct v4l2_mbus_framefmt *mbus_format =
+ v4l2_subdev_get_try_format(subdev, state, pad);
+ struct mutex *lock = &isp_dev->proc.lock;
+
+ mutex_lock(lock);
+
+ mbus_format->code = sun6i_isp_proc_formats[0].mbus_code;
+ mbus_format->width = 1280;
+ mbus_format->height = 720;
+
+ sun6i_isp_proc_mbus_format_prepare(mbus_format);
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static int
+sun6i_isp_proc_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code_enum)
+{
+ if (code_enum->index >= ARRAY_SIZE(sun6i_isp_proc_formats))
+ return -EINVAL;
+
+ code_enum->code = sun6i_isp_proc_formats[code_enum->index].mbus_code;
+
+ return 0;
+}
+
+static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev);
+ struct v4l2_mbus_framefmt *mbus_format = &format->format;
+ struct mutex *lock = &isp_dev->proc.lock;
+
+ mutex_lock(lock);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ *mbus_format = *v4l2_subdev_get_try_format(subdev, state,
+ format->pad);
+ else
+ *mbus_format = isp_dev->proc.mbus_format;
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev);
+ struct v4l2_mbus_framefmt *mbus_format = &format->format;
+ struct mutex *lock = &isp_dev->proc.lock;
+
+ mutex_lock(lock);
+
+ sun6i_isp_proc_mbus_format_prepare(mbus_format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ *v4l2_subdev_get_try_format(subdev, state, format->pad) =
+ *mbus_format;
+ else
+ isp_dev->proc.mbus_format = *mbus_format;
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = {
+ .init_cfg = sun6i_isp_proc_init_cfg,
+ .enum_mbus_code = sun6i_isp_proc_enum_mbus_code,
+ .get_fmt = sun6i_isp_proc_get_fmt,
+ .set_fmt = sun6i_isp_proc_set_fmt,
+};
+
+const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = {
+ .video = &sun6i_isp_proc_video_ops,
+ .pad = &sun6i_isp_proc_pad_ops,
+};
+
+/* Media Entity */
+
+static const struct media_entity_operations sun6i_isp_proc_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* V4L2 Async */
+
+static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev,
+ int sink_pad_index,
+ struct v4l2_subdev *remote_subdev, bool enabled)
+{
+ struct device *dev = isp_dev->dev;
+ struct v4l2_subdev *subdev = &isp_dev->proc.subdev;
+ struct media_entity *sink_entity = &subdev->entity;
+ struct media_entity *source_entity = &remote_subdev->entity;
+ int source_pad_index;
+ int ret;
+
+ /* Get the first remote source pad. */
+ ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (ret < 0) {
+ dev_err(dev, "missing source pad in external entity %s\n",
+ source_entity->name);
+ return -EINVAL;
+ }
+
+ source_pad_index = ret;
+
+ dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name,
+ source_pad_index, sink_entity->name, sink_pad_index);
+
+ ret = media_create_pad_link(source_entity, source_pad_index,
+ sink_entity, sink_pad_index,
+ enabled ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
+ source_entity->name, source_pad_index,
+ sink_entity->name, sink_pad_index);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *remote_subdev,
+ struct v4l2_async_subdev *async_subdev)
+{
+ struct sun6i_isp_device *isp_dev =
+ container_of(notifier, struct sun6i_isp_device, proc.notifier);
+ struct sun6i_isp_proc_async_subdev *proc_async_subdev =
+ container_of(async_subdev, struct sun6i_isp_proc_async_subdev,
+ async_subdev);
+ struct sun6i_isp_proc *proc = &isp_dev->proc;
+ struct sun6i_isp_proc_source *source = proc_async_subdev->source;
+ bool enabled;
+
+ switch (source->endpoint.base.port) {
+ case SUN6I_ISP_PORT_CSI0:
+ source = &proc->source_csi0;
+ enabled = true;
+ break;
+ case SUN6I_ISP_PORT_CSI1:
+ source = &proc->source_csi1;
+ enabled = !proc->source_csi0.expected;
+ break;
+ default:
+ break;
+ }
+
+ source->subdev = remote_subdev;
+
+ return sun6i_isp_proc_link(isp_dev, SUN6I_ISP_PROC_PAD_SINK_CSI,
+ remote_subdev, enabled);
+}
+
+static int
+sun6i_isp_proc_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct sun6i_isp_device *isp_dev =
+ container_of(notifier, struct sun6i_isp_device, proc.notifier);
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ int ret;
+
+ ret = v4l2_device_register_subdev_nodes(v4l2_dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations
+sun6i_isp_proc_notifier_ops = {
+ .bound = sun6i_isp_proc_notifier_bound,
+ .complete = sun6i_isp_proc_notifier_complete,
+};
+
+/* Processor */
+
+static int sun6i_isp_proc_source_setup(struct sun6i_isp_device *isp_dev,
+ struct sun6i_isp_proc_source *source,
+ u32 port)
+{
+ struct device *dev = isp_dev->dev;
+ struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier;
+ struct v4l2_fwnode_endpoint *endpoint = &source->endpoint;
+ struct sun6i_isp_proc_async_subdev *proc_async_subdev;
+ struct fwnode_handle *handle = NULL;
+ int ret;
+
+ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0);
+ if (!handle)
+ return -ENODEV;
+
+ ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
+ if (ret)
+ goto complete;
+
+ proc_async_subdev =
+ v4l2_async_nf_add_fwnode_remote(notifier, handle,
+ struct
+ sun6i_isp_proc_async_subdev);
+ if (IS_ERR(proc_async_subdev)) {
+ ret = PTR_ERR(proc_async_subdev);
+ goto complete;
+ }
+
+ proc_async_subdev->source = source;
+
+ source->expected = true;
+
+complete:
+ fwnode_handle_put(handle);
+
+ return ret;
+}
+
+int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev)
+{
+ struct device *dev = isp_dev->dev;
+ struct sun6i_isp_proc *proc = &isp_dev->proc;
+ struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
+ struct v4l2_async_notifier *notifier = &proc->notifier;
+ struct v4l2_subdev *subdev = &proc->subdev;
+ struct media_pad *pads = proc->pads;
+ int ret;
+
+ mutex_init(&proc->lock);
+
+ /* V4L2 Subdev */
+
+ v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops);
+ strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name));
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ subdev->owner = THIS_MODULE;
+ subdev->dev = dev;
+
+ v4l2_set_subdevdata(subdev, isp_dev);
+
+ /* Media Entity */
+
+ subdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
+ subdev->entity.ops = &sun6i_isp_proc_entity_ops;
+
+ /* Media Pads */
+
+ pads[SUN6I_ISP_PROC_PAD_SINK_CSI].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[SUN6I_ISP_PROC_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[SUN6I_ISP_PROC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&subdev->entity, SUN6I_ISP_PROC_PAD_COUNT,
+ pads);
+ if (ret)
+ return ret;
+
+ /* V4L2 Subdev */
+
+ ret = v4l2_device_register_subdev(v4l2_dev, subdev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to register v4l2 subdev: %d\n", ret);
+ goto error_media_entity;
+ }
+
+ /* V4L2 Async */
+
+ v4l2_async_nf_init(notifier);
+ notifier->ops = &sun6i_isp_proc_notifier_ops;
+
+ sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0,
+ SUN6I_ISP_PORT_CSI0);
+ sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1,
+ SUN6I_ISP_PORT_CSI1);
+
+ ret = v4l2_async_nf_register(v4l2_dev, notifier);
+ if (ret) {
+ v4l2_err(v4l2_dev,
+ "failed to register v4l2 async notifier: %d\n", ret);
+ goto error_v4l2_async_notifier;
+ }
+
+ return 0;
+
+error_v4l2_async_notifier:
+ v4l2_async_nf_cleanup(notifier);
+
+ v4l2_device_unregister_subdev(subdev);
+
+error_media_entity:
+ media_entity_cleanup(&subdev->entity);
+
+ return ret;
+}
+
+void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev)
+{
+ struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier;
+ struct v4l2_subdev *subdev = &isp_dev->proc.subdev;
+
+ v4l2_async_nf_unregister(notifier);
+ v4l2_async_nf_cleanup(notifier);
+
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+}
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h
new file mode 100644
index 000000000000..c5c274e21ad5
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef _SUN6I_ISP_PROC_H_
+#define _SUN6I_ISP_PROC_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define SUN6I_ISP_PROC_NAME "sun6i-isp-proc"
+
+enum sun6i_isp_proc_pad {
+ SUN6I_ISP_PROC_PAD_SINK_CSI = 0,
+ SUN6I_ISP_PROC_PAD_SINK_PARAMS = 1,
+ SUN6I_ISP_PROC_PAD_SOURCE = 2,
+ SUN6I_ISP_PROC_PAD_COUNT = 3,
+};
+
+struct sun6i_isp_device;
+
+struct sun6i_isp_proc_format {
+ u32 mbus_code;
+ u8 input_format;
+ u8 input_yuv_seq;
+};
+
+struct sun6i_isp_proc_source {
+ struct v4l2_subdev *subdev;
+ struct v4l2_fwnode_endpoint endpoint;
+ bool expected;
+};
+
+struct sun6i_isp_proc_async_subdev {
+ struct v4l2_async_subdev async_subdev;
+ struct sun6i_isp_proc_source *source;
+};
+
+struct sun6i_isp_proc {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[3];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_mbus_framefmt mbus_format;
+ struct mutex lock; /* Mbus format lock. */
+
+ struct sun6i_isp_proc_source source_csi0;
+ struct sun6i_isp_proc_source source_csi1;
+};
+
+/* Helpers */
+
+void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev,
+ unsigned int *width, unsigned int *height);
+
+/* Format */
+
+const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code);
+
+/* Proc */
+
+int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev);
+void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev);
+
+#endif
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h
new file mode 100644
index 000000000000..83b9cdab2134
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#ifndef _SUN6I_ISP_REG_H_
+#define _SUN6I_ISP_REG_H_
+
+#include <linux/kernel.h>
+
+#define SUN6I_ISP_ADDR_VALUE(a) ((a) >> 2)
+
+/* Frontend */
+
+#define SUN6I_ISP_SRC_MODE_DRAM 0
+#define SUN6I_ISP_SRC_MODE_CSI(n) (1 + (n))
+
+#define SUN6I_ISP_FE_CFG_REG 0x0
+#define SUN6I_ISP_FE_CFG_EN BIT(0)
+#define SUN6I_ISP_FE_CFG_SRC0_MODE(v) (((v) << 8) & GENMASK(9, 8))
+#define SUN6I_ISP_FE_CFG_SRC1_MODE(v) (((v) << 16) & GENMASK(17, 16))
+
+#define SUN6I_ISP_FE_CTRL_REG 0x4
+#define SUN6I_ISP_FE_CTRL_SCAP_EN BIT(0)
+#define SUN6I_ISP_FE_CTRL_VCAP_EN BIT(1)
+#define SUN6I_ISP_FE_CTRL_PARA_READY BIT(2)
+#define SUN6I_ISP_FE_CTRL_LUT_UPDATE BIT(3)
+#define SUN6I_ISP_FE_CTRL_LENS_UPDATE BIT(4)
+#define SUN6I_ISP_FE_CTRL_GAMMA_UPDATE BIT(5)
+#define SUN6I_ISP_FE_CTRL_DRC_UPDATE BIT(6)
+#define SUN6I_ISP_FE_CTRL_DISC_UPDATE BIT(7)
+#define SUN6I_ISP_FE_CTRL_OUTPUT_SPEED_CTRL(v) (((v) << 16) & GENMASK(17, 16))
+#define SUN6I_ISP_FE_CTRL_VCAP_READ_START BIT(31)
+
+#define SUN6I_ISP_FE_INT_EN_REG 0x8
+#define SUN6I_ISP_FE_INT_EN_FINISH BIT(0)
+#define SUN6I_ISP_FE_INT_EN_START BIT(1)
+#define SUN6I_ISP_FE_INT_EN_PARA_SAVE BIT(2)
+#define SUN6I_ISP_FE_INT_EN_PARA_LOAD BIT(3)
+#define SUN6I_ISP_FE_INT_EN_SRC0_FIFO BIT(4)
+#define SUN6I_ISP_FE_INT_EN_SRC1_FIFO BIT(5)
+#define SUN6I_ISP_FE_INT_EN_ROT_FINISH BIT(6)
+#define SUN6I_ISP_FE_INT_EN_LINE_NUM_START BIT(7)
+
+#define SUN6I_ISP_FE_INT_STA_REG 0xc
+#define SUN6I_ISP_FE_INT_STA_CLEAR 0xff
+#define SUN6I_ISP_FE_INT_STA_FINISH BIT(0)
+#define SUN6I_ISP_FE_INT_STA_START BIT(1)
+#define SUN6I_ISP_FE_INT_STA_PARA_SAVE BIT(2)
+#define SUN6I_ISP_FE_INT_STA_PARA_LOAD BIT(3)
+#define SUN6I_ISP_FE_INT_STA_SRC0_FIFO BIT(4)
+#define SUN6I_ISP_FE_INT_STA_SRC1_FIFO BIT(5)
+#define SUN6I_ISP_FE_INT_STA_ROT_FINISH BIT(6)
+#define SUN6I_ISP_FE_INT_STA_LINE_NUM_START BIT(7)
+
+/* Only since sun9i-a80-isp. */
+#define SUN6I_ISP_FE_INT_LINE_NUM_REG 0x18
+#define SUN6I_ISP_FE_ROT_OF_CFG_REG 0x1c
+
+/* Buffers/tables */
+
+#define SUN6I_ISP_REG_LOAD_ADDR_REG 0x20
+#define SUN6I_ISP_REG_SAVE_ADDR_REG 0x24
+
+#define SUN6I_ISP_LUT_TABLE_ADDR_REG 0x28
+#define SUN6I_ISP_DRC_TABLE_ADDR_REG 0x2c
+#define SUN6I_ISP_STATS_ADDR_REG 0x30
+
+/* SRAM */
+
+#define SUN6I_ISP_SRAM_RW_OFFSET_REG 0x38
+#define SUN6I_ISP_SRAM_RW_DATA_REG 0x3c
+
+/* Global */
+
+#define SUN6I_ISP_MODULE_EN_REG 0x40
+#define SUN6I_ISP_MODULE_EN_AE BIT(0)
+#define SUN6I_ISP_MODULE_EN_OBC BIT(1)
+#define SUN6I_ISP_MODULE_EN_DPC_LUT BIT(2)
+#define SUN6I_ISP_MODULE_EN_DPC_OTF BIT(3)
+#define SUN6I_ISP_MODULE_EN_BDNF BIT(4)
+#define SUN6I_ISP_MODULE_EN_AWB BIT(6)
+#define SUN6I_ISP_MODULE_EN_WB BIT(7)
+#define SUN6I_ISP_MODULE_EN_LSC BIT(8)
+#define SUN6I_ISP_MODULE_EN_BGC BIT(9)
+#define SUN6I_ISP_MODULE_EN_SAP BIT(10)
+#define SUN6I_ISP_MODULE_EN_AF BIT(11)
+#define SUN6I_ISP_MODULE_EN_RGB2RGB BIT(12)
+#define SUN6I_ISP_MODULE_EN_RGB_DRC BIT(13)
+#define SUN6I_ISP_MODULE_EN_TDNF BIT(15)
+#define SUN6I_ISP_MODULE_EN_AFS BIT(16)
+#define SUN6I_ISP_MODULE_EN_HIST BIT(17)
+#define SUN6I_ISP_MODULE_EN_YUV_GAIN_OFFSET BIT(18)
+#define SUN6I_ISP_MODULE_EN_YUV_DRC BIT(19)
+#define SUN6I_ISP_MODULE_EN_TG BIT(20)
+#define SUN6I_ISP_MODULE_EN_ROT BIT(21)
+#define SUN6I_ISP_MODULE_EN_CONTRAST BIT(22)
+#define SUN6I_ISP_MODULE_EN_SATU BIT(24)
+#define SUN6I_ISP_MODULE_EN_SRC1 BIT(30)
+#define SUN6I_ISP_MODULE_EN_SRC0 BIT(31)
+
+#define SUN6I_ISP_MODE_REG 0x44
+#define SUN6I_ISP_MODE_INPUT_FMT(v) ((v) & GENMASK(2, 0))
+#define SUN6I_ISP_MODE_INPUT_YUV_SEQ(v) (((v) << 3) & GENMASK(4, 3))
+#define SUN6I_ISP_MODE_OTF_DPC(v) (((v) << 16) & BIT(16))
+#define SUN6I_ISP_MODE_SHARP(v) (((v) << 17) & BIT(17))
+#define SUN6I_ISP_MODE_HIST(v) (((v) << 20) & GENMASK(21, 20))
+
+#define SUN6I_ISP_INPUT_FMT_YUV420 0
+#define SUN6I_ISP_INPUT_FMT_YUV422 1
+#define SUN6I_ISP_INPUT_FMT_RAW_BGGR 4
+#define SUN6I_ISP_INPUT_FMT_RAW_RGGB 5
+#define SUN6I_ISP_INPUT_FMT_RAW_GBRG 6
+#define SUN6I_ISP_INPUT_FMT_RAW_GRBG 7
+
+#define SUN6I_ISP_INPUT_YUV_SEQ_YUYV 0
+#define SUN6I_ISP_INPUT_YUV_SEQ_YVYU 1
+#define SUN6I_ISP_INPUT_YUV_SEQ_UYVY 2
+#define SUN6I_ISP_INPUT_YUV_SEQ_VYUY 3
+
+#define SUN6I_ISP_IN_CFG_REG 0x48
+#define SUN6I_ISP_IN_CFG_STRIDE_DIV16(v) ((v) & GENMASK(10, 0))
+
+#define SUN6I_ISP_IN_LUMA_RGB_ADDR0_REG 0x4c
+#define SUN6I_ISP_IN_CHROMA_ADDR0_REG 0x50
+#define SUN6I_ISP_IN_LUMA_RGB_ADDR1_REG 0x54
+#define SUN6I_ISP_IN_CHROMA_ADDR1_REG 0x58
+
+/* AE */
+
+#define SUN6I_ISP_AE_CFG_REG 0x60
+#define SUN6I_ISP_AE_CFG_LOW_BRI_TH(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_AE_CFG_HORZ_NUM(v) (((v) << 12) & GENMASK(15, 12))
+#define SUN6I_ISP_AE_CFG_HIGH_BRI_TH(v) (((v) << 16) & GENMASK(27, 16))
+#define SUN6I_ISP_AE_CFG_VERT_NUM(v) (((v) << 28) & GENMASK(31, 28))
+
+#define SUN6I_ISP_AE_SIZE_REG 0x64
+#define SUN6I_ISP_AE_SIZE_WIDTH(v) ((v) & GENMASK(10, 0))
+#define SUN6I_ISP_AE_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(26, 16))
+
+#define SUN6I_ISP_AE_POS_REG 0x68
+#define SUN6I_ISP_AE_POS_HORZ_START(v) ((v) & GENMASK(10, 0))
+#define SUN6I_ISP_AE_POS_VERT_START(v) (((v) << 16) & GENMASK(26, 16))
+
+/* OB */
+
+#define SUN6I_ISP_OB_SIZE_REG 0x78
+#define SUN6I_ISP_OB_SIZE_WIDTH(v) ((v) & GENMASK(13, 0))
+#define SUN6I_ISP_OB_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(29, 16))
+
+#define SUN6I_ISP_OB_VALID_REG 0x7c
+#define SUN6I_ISP_OB_VALID_WIDTH(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_OB_VALID_HEIGHT(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_OB_SRC0_VALID_START_REG 0x80
+#define SUN6I_ISP_OB_SRC0_VALID_START_HORZ(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_OB_SRC0_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_OB_SRC1_VALID_START_REG 0x84
+#define SUN6I_ISP_OB_SRC1_VALID_START_HORZ(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_OB_SRC1_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_OB_SPRITE_REG 0x88
+#define SUN6I_ISP_OB_SPRITE_WIDTH(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_OB_SPRITE_HEIGHT(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_OB_SPRITE_START_REG 0x8c
+#define SUN6I_ISP_OB_SPRITE_START_HORZ(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_OB_SPRITE_START_VERT(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_OB_CFG_REG 0x90
+#define SUN6I_ISP_OB_HORZ_POS_REG 0x94
+#define SUN6I_ISP_OB_VERT_PARA_REG 0x98
+#define SUN6I_ISP_OB_OFFSET_FIXED_REG 0x9c
+
+/* BDNF */
+
+#define SUN6I_ISP_BDNF_CFG_REG 0xcc
+#define SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(v) ((v) & GENMASK(7, 0))
+#define SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(v) (((v) << 16) & GENMASK(23, 16))
+
+#define SUN6I_ISP_BDNF_COEF_RB_REG 0xd0
+#define SUN6I_ISP_BDNF_COEF_RB(i, v) (((v) << (4 * (i))) & \
+ GENMASK(4 * (i) + 3, 4 * (i)))
+
+#define SUN6I_ISP_BDNF_COEF_G_REG 0xd4
+#define SUN6I_ISP_BDNF_COEF_G(i, v) (((v) << (4 * (i))) & \
+ GENMASK(4 * (i) + 3, 4 * (i)))
+
+/* Bayer */
+
+#define SUN6I_ISP_BAYER_OFFSET0_REG 0xe0
+#define SUN6I_ISP_BAYER_OFFSET0_R(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_BAYER_OFFSET0_GR(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_BAYER_OFFSET1_REG 0xe4
+#define SUN6I_ISP_BAYER_OFFSET1_GB(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_BAYER_OFFSET1_B(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_BAYER_GAIN0_REG 0xe8
+#define SUN6I_ISP_BAYER_GAIN0_R(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_BAYER_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_BAYER_GAIN1_REG 0xec
+#define SUN6I_ISP_BAYER_GAIN1_GB(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_BAYER_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16))
+
+/* WB */
+
+#define SUN6I_ISP_WB_GAIN0_REG 0x140
+#define SUN6I_ISP_WB_GAIN0_R(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_WB_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_WB_GAIN1_REG 0x144
+#define SUN6I_ISP_WB_GAIN1_GB(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_WB_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16))
+
+#define SUN6I_ISP_WB_CFG_REG 0x148
+#define SUN6I_ISP_WB_CFG_CLIP(v) ((v) & GENMASK(11, 0))
+
+/* Global */
+
+#define SUN6I_ISP_MCH_SIZE_CFG_REG 0x1e0
+#define SUN6I_ISP_MCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_MCH_SCALE_CFG_REG 0x1e4
+#define SUN6I_ISP_MCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_MCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16))
+#define SUN6I_ISP_MCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28))
+
+#define SUN6I_ISP_SCH_SIZE_CFG_REG 0x1e8
+#define SUN6I_ISP_SCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0))
+#define SUN6I_ISP_SCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16))
+
+#define SUN6I_ISP_SCH_SCALE_CFG_REG 0x1ec
+#define SUN6I_ISP_SCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0))
+#define SUN6I_ISP_SCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16))
+#define SUN6I_ISP_SCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28))
+
+#define SUN6I_ISP_MCH_CFG_REG 0x1f0
+#define SUN6I_ISP_MCH_CFG_EN BIT(0)
+#define SUN6I_ISP_MCH_CFG_SCALE_EN BIT(1)
+#define SUN6I_ISP_MCH_CFG_OUTPUT_FMT(v) (((v) << 2) & GENMASK(4, 2))
+#define SUN6I_ISP_MCH_CFG_MIRROR_EN BIT(5)
+#define SUN6I_ISP_MCH_CFG_FLIP_EN BIT(6)
+#define SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(v) (((v) << 8) & GENMASK(18, 8))
+#define SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(v) (((v) << 20) & GENMASK(30, 20))
+
+#define SUN6I_ISP_OUTPUT_FMT_YUV420SP 0
+#define SUN6I_ISP_OUTPUT_FMT_YUV422SP 1
+#define SUN6I_ISP_OUTPUT_FMT_YVU420SP 2
+#define SUN6I_ISP_OUTPUT_FMT_YVU422SP 3
+#define SUN6I_ISP_OUTPUT_FMT_YUV420P 4
+#define SUN6I_ISP_OUTPUT_FMT_YUV422P 5
+#define SUN6I_ISP_OUTPUT_FMT_YVU420P 6
+#define SUN6I_ISP_OUTPUT_FMT_YVU422P 7
+
+#define SUN6I_ISP_SCH_CFG_REG 0x1f4
+
+#define SUN6I_ISP_MCH_Y_ADDR0_REG 0x1f8
+#define SUN6I_ISP_MCH_U_ADDR0_REG 0x1fc
+#define SUN6I_ISP_MCH_V_ADDR0_REG 0x200
+#define SUN6I_ISP_MCH_Y_ADDR1_REG 0x204
+#define SUN6I_ISP_MCH_U_ADDR1_REG 0x208
+#define SUN6I_ISP_MCH_V_ADDR1_REG 0x20c
+#define SUN6I_ISP_SCH_Y_ADDR0_REG 0x210
+#define SUN6I_ISP_SCH_U_ADDR0_REG 0x214
+#define SUN6I_ISP_SCH_V_ADDR0_REG 0x218
+#define SUN6I_ISP_SCH_Y_ADDR1_REG 0x21c
+#define SUN6I_ISP_SCH_U_ADDR1_REG 0x220
+#define SUN6I_ISP_SCH_V_ADDR1_REG 0x224
+
+#endif
diff --git a/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h
new file mode 100644
index 000000000000..19c24c5fd322
--- /dev/null
+++ b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */
+/*
+ * Allwinner A31 ISP Configuration
+ */
+
+#ifndef _UAPI_SUN6I_ISP_CONFIG_H
+#define _UAPI_SUN6I_ISP_CONFIG_H
+
+#include <linux/types.h>
+
+#define V4L2_META_FMT_SUN6I_ISP_PARAMS v4l2_fourcc('S', '6', 'I', 'P') /* Allwinner A31 ISP Parameters */
+
+#define SUN6I_ISP_MODULE_BAYER (1U << 0)
+#define SUN6I_ISP_MODULE_BDNF (1U << 1)
+
+struct sun6i_isp_params_config_bayer {
+ __u16 offset_r;
+ __u16 offset_gr;
+ __u16 offset_gb;
+ __u16 offset_b;
+
+ __u16 gain_r;
+ __u16 gain_gr;
+ __u16 gain_gb;
+ __u16 gain_b;
+};
+
+struct sun6i_isp_params_config_bdnf {
+ __u8 in_dis_min;
+ __u8 in_dis_max;
+
+ __u8 coefficients_g[7];
+ __u8 coefficients_rb[5];
+};
+
+struct sun6i_isp_params_config {
+ __u32 modules_used;
+
+ struct sun6i_isp_params_config_bayer bayer;
+ struct sun6i_isp_params_config_bdnf bdnf;
+};
+
+#endif /* _UAPI_SUN6I_ISP_CONFIG_H */
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index b26e44adb2be..426e653bd55d 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -433,7 +433,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi,
for (i = 0; i < chan->numgangports; i++)
chan->csi_port_nums[i] = port_num + i * CSI_PORTS_PER_BRICK;
- chan->of_node = node;
+ chan->of_node = of_node_get(node);
chan->numpads = num_pads;
if (num_pads & 0x2) {
chan->pads[0].flags = MEDIA_PAD_FL_SINK;
@@ -448,6 +448,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi,
chan->mipi = tegra_mipi_request(csi->dev, node);
if (IS_ERR(chan->mipi)) {
ret = PTR_ERR(chan->mipi);
+ chan->mipi = NULL;
dev_err(csi->dev, "failed to get mipi device: %d\n", ret);
}
@@ -640,6 +641,7 @@ static void tegra_csi_channels_cleanup(struct tegra_csi *csi)
media_entity_cleanup(&subdev->entity);
}
+ of_node_put(chan->of_node);
list_del(&chan->list);
kfree(chan);
}
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 4ee05a1785cf..6960ea2e3d36 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -56,7 +56,7 @@ struct tegra_csi;
* @framerate: active framerate for TPG
* @h_blank: horizontal blanking for TPG active format
* @v_blank: vertical blanking for TPG active format
- * @mipi: mipi device for corresponding csi channel pads
+ * @mipi: mipi device for corresponding csi channel pads, or NULL if not applicable (TPG, error)
* @pixel_rate: active pixel rate from the sensor on this channel
*/
struct tegra_csi_channel {
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 9d46a36cc014..11dd142c98c5 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1811,7 +1811,7 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan,
}
/* skip entities that are already processed */
- if (remote == dev_fwnode(vi->dev) ||
+ if (device_match_fwnode(vi->dev, remote) ||
tegra_vi_graph_find_entity(chan, remote)) {
fwnode_handle_put(remote);
continue;
diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dtso
index 096137fcd5cc..096137fcd5cc 100644
--- a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts
+++ b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dtso
diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433.txt b/drivers/staging/pi433/Documentation/devicetree/pi433.txt
index a810548c5857..d317c0ec3419 100644
--- a/drivers/staging/pi433/Documentation/devicetree/pi433.txt
+++ b/drivers/staging/pi433/Documentation/devicetree/pi433.txt
@@ -48,13 +48,13 @@ For Raspbian users only
=======================
Since Raspbian supports device tree overlays, you may use an overlay instead
of editing your boards device tree.
-To use the overlay, you need to compile the file pi433-overlay.dts which can
+To use the overlay, you need to compile the file pi433-overlay.dtso which can
be found alongside this documentation.
The file needs to be compiled - either manually or by integration in your kernel
source tree. For a manual compile, you may use a command line like the following:
-'linux/scripts/dtc/dtc -@ -I dts -O dtb -o pi433.dtbo pi433-overlay.dts'
+'linux/scripts/dtc/dtc -@ -I dts -O dtb -o pi433.dtbo pi433-overlay.dtso'
-For compiling inside of the kernel tree, you need to copy pi433-overlay.dts to
+For compiling inside of the kernel tree, you need to copy pi433-overlay.dtso to
arch/arm/boot/dts/overlays and you need to add the file to the list of files
in the Makefile over there. Execute 'make dtbs' in kernel tree root to make the
kernel make files compile the device tree overlay for you.
diff --git a/drivers/staging/sm750fb/Kconfig b/drivers/staging/sm750fb/Kconfig
index 8c0d8a873d5b..acb6c08d09dc 100644
--- a/drivers/staging/sm750fb/Kconfig
+++ b/drivers/staging/sm750fb/Kconfig
@@ -6,6 +6,7 @@ config FB_SM750
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select VIDEO_NOMODESET
help
Frame buffer driver for the Silicon Motion SM750 chip
with 2D accelearion and dual head support.
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 168ae2e9005d..effc7fcc3703 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -1168,6 +1168,9 @@ static int __init lynxfb_init(void)
{
char *option;
+ if (fb_modesetting_disabled("sm750fb"))
+ return -ENODEV;
+
#ifdef MODULE
option = g_option;
#else
diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c
index 02fdef7a16c8..c7cd54171d99 100644
--- a/drivers/staging/wlan-ng/hfa384x_usb.c
+++ b/drivers/staging/wlan-ng/hfa384x_usb.c
@@ -1116,8 +1116,8 @@ cleanup:
if (ctlx == get_active_ctlx(hw)) {
spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
- del_singleshot_timer_sync(&hw->reqtimer);
- del_singleshot_timer_sync(&hw->resptimer);
+ del_timer_sync(&hw->reqtimer);
+ del_timer_sync(&hw->resptimer);
hw->req_timer_done = 1;
hw->resp_timer_done = 1;
usb_kill_urb(&hw->ctlx_urb);
diff --git a/drivers/staging/wlan-ng/prism2usb.c b/drivers/staging/wlan-ng/prism2usb.c
index e13da7fadfff..c13f1699e5a2 100644
--- a/drivers/staging/wlan-ng/prism2usb.c
+++ b/drivers/staging/wlan-ng/prism2usb.c
@@ -170,9 +170,9 @@ static void prism2sta_disconnect_usb(struct usb_interface *interface)
*/
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
- del_singleshot_timer_sync(&hw->throttle);
- del_singleshot_timer_sync(&hw->reqtimer);
- del_singleshot_timer_sync(&hw->resptimer);
+ del_timer_sync(&hw->throttle);
+ del_timer_sync(&hw->reqtimer);
+ del_timer_sync(&hw->resptimer);
/* Unlink all the URBs. This "removes the wheels"
* from the entire CTLX handling mechanism.