* [PATCH v6 00/17] Rockchip ISP1 Driver
@ 2018-03-08 9:47 Jacob Chen
2018-03-08 9:47 ` [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format Jacob Chen
` (20 more replies)
0 siblings, 21 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
changes in V6:
- add mipi txrx phy support
- remove bool and enum from uapi header
- add buf_prepare op
- correct some spelling problems
- return all queued buffers when starting stream failed
changes in V5: Sync with local changes,
- fix the SP height limit
- speed up the second stream capture
- the second stream can't force sync for rsz when start/stop streaming
- add frame id to param vb2 buf
- enable luminance maximum threshold
changes in V4:
- fix some bugs during development
- move quantization settings to rkisp1 subdev
- correct some spelling problems
- describe ports in dt-binding documents
changes in V3:
- add some comments
- fix wrong use of v4l2_async_subdev_notifier_register
- optimize two paths capture at a time
- remove compose
- re-struct headers
- add a tmp wiki page: http://opensource.rock-chips.com/wiki_Rockchip-isp1
changes in V2:
mipi-phy:
- use async probing
- make it be a child device of the GRF
isp:
- add dummy buffer
- change the way to get bus configuration, which make it possible to
add parallel sensor support in the future(without mipi-phy driver).
This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399 SoC.
Wiki Pages:
http://opensource.rock-chips.com/wiki_Rockchip-isp1
The deprecated g_mbus_config op is not dropped in V6 because i am waiting tomasz's patches.
v4l2-compliance for V6(isp params/stats nodes are passed):
v4l2-compliance SHA : 93dc5f20727fede5097d67f8b9adabe4b8046d5b
Compliance test for device /dev/video0:
Driver Info:
Driver name : rkisp1
Card type : rkisp1
Bus info : platform:ff910000.isp
Driver version : 4.16.0
Capabilities : 0x84201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Media Driver Info:
Driver name : rkisp1
Model : rkisp1
Serial :
Bus info :
Media version : 4.16.0
Hardware revision: 0x00000000 (0)
Driver version : 4.16.0
Interface Info:
ID : 0x03000007
Type : V4L Video
Entity Info:
ID : 0x00000006 (6)
Name : rkisp1_selfpath
Function : V4L2 I/O
Pad 0x01000009 : Sink
Link 0x02000021: from remote pad 0x1000004 of entity 'rkisp1-isp-subdev': Data, Enabled
Required ioctls:
test MC information (see 'Media Driver Info' above): OK
test VIDIOC_QUERYCAP: OK
Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 9 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
fail: v4l2-test-formats.cpp(330): !colorspace
fail: v4l2-test-formats.cpp(454): testColorspace(pix_mp.pixelformat, pix_mp.colorspace, pix_mp.ycbcr_enc, pix_m
p.quantization)
test VIDIOC_G_FMT: FAIL
test VIDIOC_TRY_FMT: OK (Not Supported)
test VIDIOC_S_FMT: OK (Not Supported)
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
fail: v4l2-test-formats.cpp(1288): doioctl(node, VIDIOC_G_SELECTION, &sel) != EINVAL
test Cropping: FAIL
test Composing: OK (Not Supported)
test Scaling: OK
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
fail: v4l2-test-buffers.cpp(525): VIDIOC_EXPBUF is supported, but the V4L2_MEMORY_MMAP support is missing, prob
ably due to earlier failing format tests.
test VIDIOC_EXPBUF: OK (Not Supported)
Total: 44, Succeeded: 42, Failed: 2, Warnings: 0
Jacob Chen (12):
media: doc: add document for rkisp1 meta buffer format
media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
media: rkisp1: add Rockchip ISP1 subdev driver
media: rkisp1: add ISP1 statistics driver
media: rkisp1: add ISP1 params driver
media: rkisp1: add capture device driver
media: rkisp1: add rockchip isp1 core driver
dt-bindings: Document the Rockchip ISP1 bindings
dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
ARM: dts: rockchip: add isp node for rk3288
ARM: dts: rockchip: add rx0 mipi-phy for rk3288
MAINTAINERS: add entry for Rockchip ISP1 driver
Jeffy Chen (1):
media: rkisp1: Add user space ABI definitions
Shunqian Zheng (3):
media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
arm64: dts: rockchip: add isp0 node for rk3399
arm64: dts: rockchip: add rx0 mipi-phy for rk3399
Wen Nuan (1):
ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
.../devicetree/bindings/media/rockchip-isp1.txt | 69 +
.../bindings/media/rockchip-mipi-dphy.txt | 90 +
Documentation/media/uapi/v4l/meta-formats.rst | 2 +
.../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
.../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
MAINTAINERS | 10 +
arch/arm/boot/dts/rk3288.dtsi | 33 +
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
drivers/media/platform/Kconfig | 10 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/rockchip/isp1/Makefile | 8 +
drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
drivers/media/platform/rockchip/isp1/capture.h | 167 ++
drivers/media/platform/rockchip/isp1/common.h | 110 ++
drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
drivers/media/platform/rockchip/isp1/dev.h | 93 ++
drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
.../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
.../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
drivers/media/platform/rockchip/isp1/regs.c | 239 +++
drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
include/uapi/linux/rkisp1-config.h | 798 +++++++++
include/uapi/linux/videodev2.h | 4 +
29 files changed, 9945 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
create mode 100644 drivers/media/platform/rockchip/isp1/common.h
create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
create mode 100644 include/uapi/linux/rkisp1-config.h
--
2.16.1
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2019-04-26 20:34 ` Nicolas Dufresne
2018-03-08 9:47 ` [PATCH v6 02/17] media: doc: add document for " Jacob Chen
` (19 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Shunqian Zheng <zhengsq@rock-chips.com>
Add the Rockchip ISP1 specific processing parameter format
V4L2_META_FMT_RK_ISP1_PARAMS and metadata format
V4L2_META_FMT_RK_ISP1_STAT_3A for 3A.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
include/uapi/linux/videodev2.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 1be54429d601..bdd469290d66 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1257,6 +1257,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break;
case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
case V4L2_META_FMT_UVC: descr = "UVC payload header metadata"; break;
+ case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A params"; break;
+ case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A statistics"; break;
default:
/* Compressed formats */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index e20d10df75c1..359ad8bbf9bf 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -698,6 +698,10 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_VSP1_HGT v4l2_fourcc('V', 'S', 'P', 'T') /* R-Car VSP1 2-D Histogram */
#define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */
+/* Vendor specific - used for IPU3 camera sub-system */
+#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */
+#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A statistics */
+
/* priv field value to indicates that subsequent fields are valid. */
#define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 02/17] media: doc: add document for rkisp1 meta buffer format
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
2018-03-08 9:47 ` [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 13:37 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions Jacob Chen
` (18 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
This commit add docuemnt for rkisp1 meta buffer format
Signed-off-by: Jacob Chen <jacob-chen@rock-chips.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
Documentation/media/uapi/v4l/meta-formats.rst | 2 ++
.../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 ++++++++++++++++++++
.../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 ++++++++++++++++++
3 files changed, 40 insertions(+)
create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst
index 0c4e1ecf5879..44e7edbf1dae 100644
--- a/Documentation/media/uapi/v4l/meta-formats.rst
+++ b/Documentation/media/uapi/v4l/meta-formats.rst
@@ -15,3 +15,5 @@ These formats are used for the :ref:`metadata` interface only.
pixfmt-meta-uvc
pixfmt-meta-vsp1-hgo
pixfmt-meta-vsp1-hgt
+ pixfmt-meta-rkisp1-params
+ pixfmt-meta-rkisp1-stat
diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
new file mode 100644
index 000000000000..2ff4f4309795
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
@@ -0,0 +1,20 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _v4l2-meta-fmt-rkisp1-params:
+
+*******************************
+V4L2_META_FMT_RK_ISP1_PARAMS
+*******************************
+
+Rockchip ISP1 Parameters Data
+
+Description
+===========
+
+This format describes input parameters for the Rockchip ISP1.
+
+It uses c-struct :c:type:`rkisp1_isp_params_cfg`, which is defined in
+the ``linux/rkisp1-config.h`` header file, see it for details.
+
+The parameters consist of multiple modules.
+The module won't be updated if the correspond bit was not set in module_*_update.
diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
new file mode 100644
index 000000000000..dca8befe58f1
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
@@ -0,0 +1,18 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _v4l2-meta-fmt-rkisp1-stat:
+
+*******************************
+V4L2_META_FMT_RK_ISP1_STAT_3A
+*******************************
+
+Rockchip ISP1 Statistics Data
+
+Description
+===========
+
+This format describes image color statistics information generated by the Rockchip
+ISP1.
+
+It use c-struct :c:type:`rkisp1_stat_buffer`, which is defined in
+the ``linux/cifisp_stat.h`` header file, see it for details.
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
2018-03-08 9:47 ` [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format Jacob Chen
2018-03-08 9:47 ` [PATCH v6 02/17] media: doc: add document for " Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 13:34 ` Hans Verkuil
2018-04-24 4:28 ` Tomasz Figa
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (17 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jeffy Chen <jeffy.chen@rock-chips.com>
Add the header for userspace
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
include/uapi/linux/rkisp1-config.h | 798 +++++++++++++++++++++++++++++++++++++
1 file changed, 798 insertions(+)
create mode 100644 include/uapi/linux/rkisp1-config.h
diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
new file mode 100644
index 000000000000..1284eb9fd09d
--- /dev/null
+++ b/include/uapi/linux/rkisp1-config.h
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+/*
+ * References:
+ * REF_01 - ISP_user_manual, Rev 2.57
+ */
+
+#ifndef _UAPI_RKISP1_CONFIG_H
+#define _UAPI_RKISP1_CONFIG_H
+
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+
+#define CIFISP_MODULE_DPCC (1 << 0)
+#define CIFISP_MODULE_BLS (1 << 1)
+#define CIFISP_MODULE_SDG (1 << 2)
+#define CIFISP_MODULE_HST (1 << 3)
+#define CIFISP_MODULE_LSC (1 << 4)
+#define CIFISP_MODULE_AWB_GAIN (1 << 5)
+#define CIFISP_MODULE_FLT (1 << 6)
+#define CIFISP_MODULE_BDM (1 << 7)
+#define CIFISP_MODULE_CTK (1 << 8)
+#define CIFISP_MODULE_GOC (1 << 9)
+#define CIFISP_MODULE_CPROC (1 << 10)
+#define CIFISP_MODULE_AFC (1 << 11)
+#define CIFISP_MODULE_AWB (1 << 12)
+#define CIFISP_MODULE_IE (1 << 13)
+#define CIFISP_MODULE_AEC (1 << 14)
+#define CIFISP_MODULE_WDR (1 << 15)
+#define CIFISP_MODULE_DPF (1 << 16)
+#define CIFISP_MODULE_DPF_STRENGTH (1 << 17)
+
+#define CIFISP_CTK_COEFF_MAX 0x100
+#define CIFISP_CTK_OFFSET_MAX 0x800
+
+#define CIFISP_AE_MEAN_MAX 25
+#define CIFISP_HIST_BIN_N_MAX 16
+#define CIFISP_AFM_MAX_WINDOWS 3
+#define CIFISP_DEGAMMA_CURVE_SIZE 17
+
+#define CIFISP_BDM_MAX_TH 0xFF
+
+/*
+ * Black level compensation
+ */
+/* maximum value for horizontal start address */
+#define CIFISP_BLS_START_H_MAX 0x00000FFF
+/* maximum value for horizontal stop address */
+#define CIFISP_BLS_STOP_H_MAX 0x00000FFF
+/* maximum value for vertical start address */
+#define CIFISP_BLS_START_V_MAX 0x00000FFF
+/* maximum value for vertical stop address */
+#define CIFISP_BLS_STOP_V_MAX 0x00000FFF
+/* maximum is 2^18 = 262144*/
+#define CIFISP_BLS_SAMPLES_MAX 0x00000012
+/* maximum value for fixed black level */
+#define CIFISP_BLS_FIX_SUB_MAX 0x00000FFF
+/* minimum value for fixed black level */
+#define CIFISP_BLS_FIX_SUB_MIN 0xFFFFF000
+/* 13 bit range (signed)*/
+#define CIFISP_BLS_FIX_MASK 0x00001FFF
+
+/*
+ * Automatic white balance measurments
+ */
+#define CIFISP_AWB_MAX_GRID 1
+#define CIFISP_AWB_MAX_FRAMES 7
+
+/*
+ * Gamma out
+ */
+/* Maximum number of color samples supported */
+#define CIFISP_GAMMA_OUT_MAX_SAMPLES 17
+
+/*
+ * Lens shade correction
+ */
+#define CIFISP_LSC_GRAD_TBL_SIZE 8
+#define CIFISP_LSC_SIZE_TBL_SIZE 8
+/*
+ * The following matches the tuning process,
+ * not the max capabilities of the chip.
+ * Last value unused.
+ */
+#define CIFISP_LSC_DATA_TBL_SIZE 290
+
+/*
+ * Histogram calculation
+ */
+/* Last 3 values unused. */
+#define CIFISP_HISTOGRAM_WEIGHT_GRIDS_SIZE 28
+
+/*
+ * Defect Pixel Cluster Correction
+ */
+#define CIFISP_DPCC_METHODS_MAX 3
+
+/*
+ * Denoising pre filter
+ */
+#define CIFISP_DPF_MAX_NLF_COEFFS 17
+#define CIFISP_DPF_MAX_SPATIAL_COEFFS 6
+
+/*
+ * Measurement types
+ */
+#define CIFISP_STAT_AWB (1 << 0)
+#define CIFISP_STAT_AUTOEXP (1 << 1)
+#define CIFISP_STAT_AFM_FIN (1 << 2)
+#define CIFISP_STAT_HIST (1 << 3)
+
+enum cifisp_histogram_mode {
+ CIFISP_HISTOGRAM_MODE_DISABLE,
+ CIFISP_HISTOGRAM_MODE_RGB_COMBINED,
+ CIFISP_HISTOGRAM_MODE_R_HISTOGRAM,
+ CIFISP_HISTOGRAM_MODE_G_HISTOGRAM,
+ CIFISP_HISTOGRAM_MODE_B_HISTOGRAM,
+ CIFISP_HISTOGRAM_MODE_Y_HISTOGRAM
+};
+
+enum cifisp_awb_mode_type {
+ CIFISP_AWB_MODE_MANUAL,
+ CIFISP_AWB_MODE_RGB,
+ CIFISP_AWB_MODE_YCBCR
+};
+
+enum cifisp_flt_mode {
+ CIFISP_FLT_STATIC_MODE,
+ CIFISP_FLT_DYNAMIC_MODE
+};
+
+/**
+ * enum cifisp_exp_ctrl_autostop - stop modes
+ * @CIFISP_EXP_CTRL_AUTOSTOP_0: continuous measurement
+ * @CIFISP_EXP_CTRL_AUTOSTOP_1: stop measuring after a complete frame
+ */
+enum cifisp_exp_ctrl_autostop {
+ CIFISP_EXP_CTRL_AUTOSTOP_0 = 0,
+ CIFISP_EXP_CTRL_AUTOSTOP_1 = 1,
+};
+
+/**
+ * enum cifisp_exp_meas_mode - Exposure measure mode
+ * @CIFISP_EXP_MEASURING_MODE_0: Y = 16 + 0.25R + 0.5G + 0.1094B
+ * @CIFISP_EXP_MEASURING_MODE_1: Y = (R + G + B) x (85/256)
+ */
+enum cifisp_exp_meas_mode {
+ CIFISP_EXP_MEASURING_MODE_0,
+ CIFISP_EXP_MEASURING_MODE_1,
+};
+
+/*---------- PART1: Input Parameters ------------*/
+
+struct cifisp_window {
+ __u16 h_offs;
+ __u16 v_offs;
+ __u16 h_size;
+ __u16 v_size;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_bls_fixed_val - BLS fixed subtraction values
+ *
+ * The values will be subtracted from the sensor
+ * values. Therefore a negative value means addition instead of subtraction!
+ *
+ * @r: Fixed (signed!) subtraction value for Bayer pattern R
+ * @gr: Fixed (signed!) subtraction value for Bayer pattern Gr
+ * @gb: Fixed (signed!) subtraction value for Bayer pattern Gb
+ * @b: Fixed (signed!) subtraction value for Bayer pattern B
+ */
+struct cifisp_bls_fixed_val {
+ __s16 r;
+ __s16 gr;
+ __s16 gb;
+ __s16 b;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_bls_config - Configuration used by black level subtraction
+ *
+ * @enable_auto: Automatic mode activated means that the measured values
+ * are subtracted. Otherwise the fixed subtraction
+ * values will be subtracted.
+ * @en_windows: enabled window
+ * @bls_window1: Measurement window 1 size
+ * @bls_window2: Measurement window 2 size
+ * @bls_samples: Set amount of measured pixels for each Bayer position
+ * (A, B,C and D) to 2^bls_samples.
+ * @cifisp_bls_fixed_val: Fixed subtraction values
+ */
+struct cifisp_bls_config {
+ __u8 enable_auto;
+ __u8 en_windows;
+ struct cifisp_window bls_window1;
+ struct cifisp_window bls_window2;
+ __u8 bls_samples;
+ struct cifisp_bls_fixed_val fixed_val;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpcc_methods_config - Methods Configuration used by Defect Pixel Cluster Correction
+ *
+ * @method: Method enable bits
+ * @line_thresh: Line threshold
+ * @line_mad_fac: Line MAD factor
+ * @pg_fac: Peak gradient factor
+ * @rnd_thresh: Rank Neighbor Difference threshold
+ * @rg_fac: Rank gradient factor
+ */
+struct cifisp_dpcc_methods_config {
+ __u32 method;
+ __u32 line_thresh;
+ __u32 line_mad_fac;
+ __u32 pg_fac;
+ __u32 rnd_thresh;
+ __u32 rg_fac;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpcc_methods_config - Configuration used by Defect Pixel Cluster Correction
+ *
+ * @mode: dpcc output mode
+ * @output_mode: whether use hard coded methods
+ * @set_use: stage1 methods set
+ * @methods: methods config
+ * @ro_limits: rank order limits
+ * @rnd_offs: differential rank offsets for rank neighbor difference
+ */
+struct cifisp_dpcc_config {
+ __u32 mode;
+ __u32 output_mode;
+ __u32 set_use;
+ struct cifisp_dpcc_methods_config methods[CIFISP_DPCC_METHODS_MAX];
+ __u32 ro_limits;
+ __u32 rnd_offs;
+} __attribute__ ((packed));
+
+struct cifisp_gamma_corr_curve {
+ __u16 gamma_y[CIFISP_DEGAMMA_CURVE_SIZE];
+} __attribute__ ((packed));
+
+struct cifisp_gamma_curve_x_axis_pnts {
+ __u32 gamma_dx0;
+ __u32 gamma_dx1;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_gamma_corr_curve - Configuration used by sensor degamma
+ *
+ * @curve_x: gamma curve point definition axis for x
+ * @xa_pnts: x increments
+ */
+struct cifisp_sdg_config {
+ struct cifisp_gamma_corr_curve curve_r;
+ struct cifisp_gamma_corr_curve curve_g;
+ struct cifisp_gamma_corr_curve curve_b;
+ struct cifisp_gamma_curve_x_axis_pnts xa_pnts;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_lsc_config - Configuration used by Lens shading correction
+ *
+ * refer to REF_01 for details
+ */
+struct cifisp_lsc_config {
+ __u32 r_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
+ __u32 gr_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
+ __u32 gb_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
+ __u32 b_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
+
+ __u32 x_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
+ __u32 y_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
+
+ __u32 x_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
+ __u32 y_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
+ __u16 config_width;
+ __u16 config_height;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_ie_config - Configuration used by image effects
+ *
+ * @eff_mat_1: 3x3 Matrix Coefficients for Emboss Effect 1
+ * @eff_mat_2: 3x3 Matrix Coefficients for Emboss Effect 2
+ * @eff_mat_3: 3x3 Matrix Coefficients for Emboss 3/Sketch 1
+ * @eff_mat_4: 3x3 Matrix Coefficients for Sketch Effect 2
+ * @eff_mat_5: 3x3 Matrix Coefficients for Sketch Effect 3
+ * @eff_tint: Chrominance increment values of tint (used for sepia effect)
+ */
+struct cifisp_ie_config {
+ __u16 effect;
+ __u16 color_sel;
+ __u16 eff_mat_1;
+ __u16 eff_mat_2;
+ __u16 eff_mat_3;
+ __u16 eff_mat_4;
+ __u16 eff_mat_5;
+ __u16 eff_tint;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_cproc_config - Configuration used by Color Processing
+ *
+ * @c_out_range: Chrominance pixel clipping range at output. (0 for limit, 1 for full)
+ * @y_in_range: Luminance pixel clipping range at output.
+ * @y_out_range: Luminance pixel clipping range@output.
+ * @contrast: 00~ff, 0.0~1.992
+ * @brightness: 80~7F, -128~+127
+ * @sat: saturation, 00~FF, 0.0~1.992
+ * @hue: 80~7F, -90~+87.188
+ */
+struct cifisp_cproc_config {
+ __u8 c_out_range;
+ __u8 y_in_range;
+ __u8 y_out_range;
+ __u8 contrast;
+ __u8 brightness;
+ __u8 sat;
+ __u8 hue;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_awb_meas_config - Configuration used by auto white balance
+ *
+ * @awb_wnd: white balance measurement window (in pixels) (from enum cifisp_awb_mode_type)
+ * @max_y: only pixels values < max_y contribute to awb measurement, set to 0 to disable this feature
+ * @min_y: only pixels values > min_y contribute to awb measurement
+ * @max_csum: Chrominance sum maximum value, only consider pixels with Cb+Cr,
+ * smaller than threshold for awb measurements
+ * @min_c: Chrominance minimum value, only consider pixels with Cb/Cr
+ * each greater than threshold value for awb measurements
+ * @frames: number of frames - 1 used for mean value calculation (ucFrames=0 means 1 Frame)
+ * @awb_ref_cr: reference Cr value for AWB regulation, target for AWB
+ * @awb_ref_cb: reference Cb value for AWB regulation, target for AWB
+ */
+struct cifisp_awb_meas_config {
+ /*
+ * Note: currently the h and v offsets are mapped to grid offsets
+ */
+ struct cifisp_window awb_wnd;
+ __u32 awb_mode;
+ __u8 max_y;
+ __u8 min_y;
+ __u8 max_csum;
+ __u8 min_c;
+ __u8 frames;
+ __u8 awb_ref_cr;
+ __u8 awb_ref_cb;
+ __u8 enable_ymax_cmp;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_awb_gain_config - Configuration used by auto white balance gain
+ *
+ * out_data_x = ( AWB_GEAIN_X * in_data + 128) >> 8
+ */
+struct cifisp_awb_gain_config {
+ __u16 gain_red;
+ __u16 gain_green_r;
+ __u16 gain_blue;
+ __u16 gain_green_b;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_flt_config - Configuration used by ISP filtering
+ *
+ * @mode: ISP_FILT_MODE register fields (from enum cifisp_flt_mode)
+ * @grn_stage1: ISP_FILT_MODE register fields
+ * @chr_h_mode: ISP_FILT_MODE register fields
+ * @chr_v_mode: ISP_FILT_MODE register fields
+ *
+ * refer to REF_01 for details.
+ */
+
+struct cifisp_flt_config {
+ __u32 mode;
+ __u8 grn_stage1;
+ __u8 chr_h_mode;
+ __u8 chr_v_mode;
+ __u32 thresh_bl0;
+ __u32 thresh_bl1;
+ __u32 thresh_sh0;
+ __u32 thresh_sh1;
+ __u32 lum_weight;
+ __u32 fac_sh1;
+ __u32 fac_sh0;
+ __u32 fac_mid;
+ __u32 fac_bl0;
+ __u32 fac_bl1;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_bdm_config - Configuration used by Bayer DeMosaic
+ *
+ * @demosaic_th: threshod for bayer demosaicing texture detection
+ */
+struct cifisp_bdm_config {
+ __u8 demosaic_th;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_ctk_config - Configuration used by Cross Talk correction
+ *
+ * @coeff: color correction matrix
+ * @ct_offset_b: offset for the crosstalk correction matrix
+ */
+struct cifisp_ctk_config {
+ __u16 coeff0;
+ __u16 coeff1;
+ __u16 coeff2;
+ __u16 coeff3;
+ __u16 coeff4;
+ __u16 coeff5;
+ __u16 coeff6;
+ __u16 coeff7;
+ __u16 coeff8;
+ __u16 ct_offset_r;
+ __u16 ct_offset_g;
+ __u16 ct_offset_b;
+} __attribute__ ((packed));
+
+enum cifisp_goc_mode {
+ CIFISP_GOC_MODE_LOGARITHMIC,
+ CIFISP_GOC_MODE_EQUIDISTANT
+};
+
+/**
+ * struct cifisp_goc_config - Configuration used by Gamma Out correction
+ *
+ * @mode: goc mode (from enum cifisp_goc_mode)
+ * @gamma_y: gamma out curve y-axis for all color components
+ */
+struct cifisp_goc_config {
+ __u32 mode;
+ __u16 gamma_y[CIFISP_GAMMA_OUT_MAX_SAMPLES];
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_hst_config - Configuration used by Histogram
+ *
+ * @mode: histogram mode (from enum cifisp_histogram_mode)
+ * @histogram_predivider: process every stepsize pixel, all other pixels are skipped
+ * @meas_window: coordinates of the measure window
+ * @hist_weight: weighting factor for sub-windows
+ */
+struct cifisp_hst_config {
+ __u32 mode;
+ __u8 histogram_predivider;
+ struct cifisp_window meas_window;
+ __u8 hist_weight[CIFISP_HISTOGRAM_WEIGHT_GRIDS_SIZE];
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_aec_config - Configuration used by Auto Exposure Control
+ *
+ * @mode: Exposure measure mode (from enum cifisp_exp_meas_mode)
+ * @autostop: stop mode (from enum cifisp_exp_ctrl_autostop)
+ * @meas_window: coordinates of the measure window
+ */
+struct cifisp_aec_config {
+ __u32 mode;
+ __u32 autostop;
+ struct cifisp_window meas_window;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_afc_config - Configuration used by Auto Focus Control
+ *
+ * @num_afm_win: max CIFISP_AFM_MAX_WINDOWS
+ * @afm_win: coordinates of the meas window
+ * @thres: threshold used for minimizing the influence of noise
+ * @var_shift: the number of bits for the shift operation at the end of the calculation chain.
+ */
+struct cifisp_afc_config {
+ __u8 num_afm_win;
+ struct cifisp_window afm_win[CIFISP_AFM_MAX_WINDOWS];
+ __u32 thres;
+ __u32 var_shift;
+} __attribute__ ((packed));
+
+/**
+ * enum cifisp_dpf_gain_usage - dpf gain usage
+ * @CIFISP_DPF_GAIN_USAGE_DISABLED: don't use any gains in preprocessing stage
+ * @CIFISP_DPF_GAIN_USAGE_NF_GAINS: use only the noise function gains from registers DPF_NF_GAIN_R, ...
+ * @CIFISP_DPF_GAIN_USAGE_LSC_GAINS: use only the gains from LSC module
+ * @CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS: use the noise function gains and the gains from LSC module
+ * @CIFISP_DPF_GAIN_USAGE_AWB_GAINS: use only the gains from AWB module
+ * @CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS: use the gains from AWB and LSC module
+ * @CIFISP_DPF_GAIN_USAGE_MAX: upper border (only for an internal evaluation)
+ */
+enum cifisp_dpf_gain_usage {
+ CIFISP_DPF_GAIN_USAGE_DISABLED,
+ CIFISP_DPF_GAIN_USAGE_NF_GAINS,
+ CIFISP_DPF_GAIN_USAGE_LSC_GAINS,
+ CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS,
+ CIFISP_DPF_GAIN_USAGE_AWB_GAINS,
+ CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS,
+ CIFISP_DPF_GAIN_USAGE_MAX
+};
+
+/**
+ * enum cifisp_dpf_gain_usage - dpf gain usage
+ * @CIFISP_DPF_RB_FILTERSIZE_13x9: red and blue filter kernel size 13x9 (means 7x5 active pixel)
+ * @CIFISP_DPF_RB_FILTERSIZE_9x9: red and blue filter kernel size 9x9 (means 5x5 active pixel)
+ */
+enum cifisp_dpf_rb_filtersize {
+ CIFISP_DPF_RB_FILTERSIZE_13x9,
+ CIFISP_DPF_RB_FILTERSIZE_9x9,
+};
+
+/**
+ * enum cifisp_dpf_nll_scale_mode - dpf noise level scale mode
+ * @CIFISP_NLL_SCALE_LINEAR: use a linear scaling
+ * @CIFISP_NLL_SCALE_LOGARITHMIC: use a logarithmic scaling
+ */
+enum cifisp_dpf_nll_scale_mode {
+ CIFISP_NLL_SCALE_LINEAR,
+ CIFISP_NLL_SCALE_LOGARITHMIC,
+};
+
+/**
+ * struct cifisp_dpf_nll - Noise level lookup
+ *
+ * @coeff: Noise level Lookup coefficient
+ * @scale_mode: dpf noise level scale mode (from enum cifisp_dpf_nll_scale_mode)
+ */
+struct cifisp_dpf_nll {
+ __u16 coeff[CIFISP_DPF_MAX_NLF_COEFFS];
+ __u32 scale_mode;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpf_rb_flt - Red blue filter config
+ *
+ * @fltsize: The filter size for the red and blue pixels
+ * (from enum cifisp_dpf_rb_filtersize)
+ * @spatial_coeff: Spatial weights
+ * @r_enable: enable filter processing for red pixels
+ * @b_enable: enable filter processing for blue pixels
+ */
+struct cifisp_dpf_rb_flt {
+ __u32 fltsize;
+ __u8 spatial_coeff[CIFISP_DPF_MAX_SPATIAL_COEFFS];
+ __u8 r_enable;
+ __u8 b_enable;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpf_g_flt - Green filter Configuration
+ *
+ * @spatial_coeff: Spatial weights
+ * @gr_enable: enable filter processing for green pixels in green/red lines
+ * @gb_enable: enable filter processing for green pixels in green/blue lines
+ */
+struct cifisp_dpf_g_flt {
+ __u8 spatial_coeff[CIFISP_DPF_MAX_SPATIAL_COEFFS];
+ __u8 gr_enable;
+ __u8 gb_enable;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpf_gain - Noise function Configuration
+ *
+ * @mode: dpf gain usage (from enum cifisp_dpf_gain_usage)
+ * @nf_r_gain: Noise function Gain that replaces the AWB gain for red pixels
+ * @nf_b_gain: Noise function Gain that replaces the AWB gain for blue pixels
+ * @nf_gr_gain: Noise function Gain that replaces the AWB gain
+ * for green pixels in a red line
+ * @nf_gb_gain: Noise function Gain that replaces the AWB gain
+ * for green pixels in a blue line
+ */
+struct cifisp_dpf_gain {
+ __u32 mode;
+ __u16 nf_r_gain;
+ __u16 nf_b_gain;
+ __u16 nf_gr_gain;
+ __u16 nf_gb_gain;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpf_config - Configuration used by De-noising pre-filter
+ *
+ * @gain: noise function gain
+ * @g_flt: green filter config
+ * @rb_flt: red blue filter config
+ * @nll: noise level lookup
+ */
+struct cifisp_dpf_config {
+ struct cifisp_dpf_gain gain;
+ struct cifisp_dpf_g_flt g_flt;
+ struct cifisp_dpf_rb_flt rb_flt;
+ struct cifisp_dpf_nll nll;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_dpf_strength_config - strength of the filter
+ *
+ * @r: filter strength of the RED filter
+ * @g: filter strength of the GREEN filter
+ * @b: filter strength of the BLUE filter
+ */
+struct cifisp_dpf_strength_config {
+ __u8 r;
+ __u8 g;
+ __u8 b;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_isp_other_cfg - Parameters for some blocks in rockchip isp1
+ *
+ * @dpcc_config: Defect Pixel Cluster Correction config
+ * @bls_config: Black Level Subtraction config
+ * @sdg_config: sensor degamma config
+ * @lsc_config: Lens Shade config
+ * @awb_gain_config: Auto White balance gain config
+ * @flt_config: filter config
+ * @bdm_config: demosaic config
+ * @ctk_config: cross talk config
+ * @goc_config: gamma out config
+ * @bls_config: black level subtraction config
+ * @dpf_config: De-noising pre-filter config
+ * @dpf_strength_config: dpf strength config
+ * @cproc_config: color process config
+ * @ie_config: image effects config
+ */
+struct cifisp_isp_other_cfg {
+ struct cifisp_dpcc_config dpcc_config;
+ struct cifisp_bls_config bls_config;
+ struct cifisp_sdg_config sdg_config;
+ struct cifisp_lsc_config lsc_config;
+ struct cifisp_awb_gain_config awb_gain_config;
+ struct cifisp_flt_config flt_config;
+ struct cifisp_bdm_config bdm_config;
+ struct cifisp_ctk_config ctk_config;
+ struct cifisp_goc_config goc_config;
+ struct cifisp_dpf_config dpf_config;
+ struct cifisp_dpf_strength_config dpf_strength_config;
+ struct cifisp_cproc_config cproc_config;
+ struct cifisp_ie_config ie_config;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_isp_meas_cfg - Rockchip ISP1 Measure Parameters
+ *
+ * @awb_meas_config: auto white balance config
+ * @hst_config: histogram config
+ * @aec_config: auto exposure config
+ * @afc_config: auto focus config
+ */
+struct cifisp_isp_meas_cfg {
+ struct cifisp_awb_meas_config awb_meas_config;
+ struct cifisp_hst_config hst_config;
+ struct cifisp_aec_config aec_config;
+ struct cifisp_afc_config afc_config;
+} __attribute__ ((packed));
+
+/**
+ * struct rkisp1_isp_params_cfg - Rockchip ISP1 Input Parameters Meta Data
+ *
+ * @module_en_update: mask the enable bits of which module should be updated
+ * @module_ens: mask the enable value of each module, only update the module
+ * which correspond bit was set in module_en_update
+ * @module_cfg_update: mask the config bits of which module should be updated
+ * @meas: measurement config
+ * @others: other config
+ */
+struct rkisp1_isp_params_cfg {
+ __u32 module_en_update;
+ __u32 module_ens;
+ __u32 module_cfg_update;
+
+ struct cifisp_isp_meas_cfg meas;
+ struct cifisp_isp_other_cfg others;
+} __attribute__ ((packed));
+
+/*---------- PART2: Measurement Statistics ------------*/
+
+/**
+ * struct cifisp_bls_meas_val - AWB measured values
+ *
+ * @cnt: White pixel count, number of "white pixels" found during laster measurement
+ * @mean_y_or_g: Mean value of Y within window and frames, Green if RGB is selected.
+ * @mean_cb_or_b: Mean value of Cb within window and frames, Blue if RGB is selected.
+ * @mean_cr_or_r: Mean value of Cr within window and frames, Red if RGB is selected.
+ */
+struct cifisp_awb_meas {
+ __u32 cnt;
+ __u8 mean_y_or_g;
+ __u8 mean_cb_or_b;
+ __u8 mean_cr_or_r;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_awb_stat - statistics automatic white balance data
+ *
+ * @awb_mean: Mean measured data
+ */
+struct cifisp_awb_stat {
+ struct cifisp_awb_meas awb_mean[CIFISP_AWB_MAX_GRID];
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_bls_meas_val - BLS measured values
+ *
+ * @meas_r: Mean measured value for Bayer pattern R
+ * @meas_gr: Mean measured value for Bayer pattern Gr
+ * @meas_gb: Mean measured value for Bayer pattern Gb
+ * @meas_b: Mean measured value for Bayer pattern B
+ */
+struct cifisp_bls_meas_val {
+ __u16 meas_r;
+ __u16 meas_gr;
+ __u16 meas_gb;
+ __u16 meas_b;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_ae_stat - statistics auto exposure data
+ *
+ * @exp_mean: Mean luminance value of block xx
+ * @bls_val: BLS measured values
+ *
+ * Image is divided into 5x5 blocks.
+ */
+struct cifisp_ae_stat {
+ __u8 exp_mean[CIFISP_AE_MEAN_MAX];
+ struct cifisp_bls_meas_val bls_val;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_af_meas_val - AF measured values
+ *
+ * @sum: sharpness, refer to REF_01 for definition
+ * @lum: luminance, refer to REF_01 for definition
+ */
+struct cifisp_af_meas_val {
+ __u32 sum;
+ __u32 lum;
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_af_stat - statistics auto focus data
+ *
+ * @window: AF measured value of window x
+ *
+ * The module measures the sharpness in 3 windows of selectable size via
+ * register settings(ISP_AFM_*_A/B/C)
+ */
+struct cifisp_af_stat {
+ struct cifisp_af_meas_val window[CIFISP_AFM_MAX_WINDOWS];
+} __attribute__ ((packed));
+
+/**
+ * struct cifisp_hist_stat - statistics histogram data
+ *
+ * @hist_bins: measured bin counters
+ *
+ * Measurement window divided into 25 sub-windows, set
+ * with ISP_HIST_XXX
+ */
+struct cifisp_hist_stat {
+ __u16 hist_bins[CIFISP_HIST_BIN_N_MAX];
+} __attribute__ ((packed));
+
+/**
+ * struct rkisp1_stat_buffer - Rockchip ISP1 Statistics Data
+ *
+ * @cifisp_awb_stat: statistics data for automatic white balance
+ * @cifisp_ae_stat: statistics data for auto exposure
+ * @cifisp_af_stat: statistics data for auto focus
+ * @cifisp_hist_stat: statistics histogram data
+ */
+struct cifisp_stat {
+ struct cifisp_awb_stat awb;
+ struct cifisp_ae_stat ae;
+ struct cifisp_af_stat af;
+ struct cifisp_hist_stat hist;
+} __attribute__ ((packed));
+
+/**
+ * struct rkisp1_stat_buffer - Rockchip ISP1 Statistics Meta Data
+ *
+ * @meas_type: measurement types (CIFISP_STAT_ definitions)
+ * @frame_id: frame ID for sync
+ * @params: statistics data
+ */
+struct rkisp1_stat_buffer {
+ __u32 meas_type;
+ __u32 frame_id;
+ struct cifisp_stat params;
+} __attribute__ ((packed));
+
+#endif /* _UAPI_RKISP1_CONFIG_H */
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (2 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-05-16 5:20 ` Laurent Pinchart
2019-03-10 17:49 ` Laurent Pinchart
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
` (16 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
.../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
.../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
2 files changed, 883 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
new file mode 100644
index 000000000000..32140960557a
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define RK3288_GRF_SOC_CON6 0x025c
+#define RK3288_GRF_SOC_CON8 0x0264
+#define RK3288_GRF_SOC_CON9 0x0268
+#define RK3288_GRF_SOC_CON10 0x026c
+#define RK3288_GRF_SOC_CON14 0x027c
+#define RK3288_GRF_SOC_STATUS21 0x02d4
+#define RK3288_GRF_IO_VSEL 0x0380
+#define RK3288_GRF_SOC_CON15 0x03a4
+
+#define RK3399_GRF_SOC_CON9 0x6224
+#define RK3399_GRF_SOC_CON21 0x6254
+#define RK3399_GRF_SOC_CON22 0x6258
+#define RK3399_GRF_SOC_CON23 0x625c
+#define RK3399_GRF_SOC_CON24 0x6260
+#define RK3399_GRF_SOC_CON25 0x6264
+#define RK3399_GRF_SOC_STATUS1 0xe2a4
+
+#define CLOCK_LANE_HS_RX_CONTROL 0x34
+#define LANE0_HS_RX_CONTROL 0x44
+#define LANE1_HS_RX_CONTROL 0x54
+#define LANE2_HS_RX_CONTROL 0x84
+#define LANE3_HS_RX_CONTROL 0x94
+#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
+
+/*
+ * CSI HOST
+ */
+#define CSIHOST_PHY_TEST_CTRL0 0x30
+#define CSIHOST_PHY_TEST_CTRL1 0x34
+#define CSIHOST_PHY_SHUTDOWNZ 0x08
+#define CSIHOST_DPHY_RSTZ 0x0c
+
+#define PHY_TESTEN_ADDR (0x1 << 16)
+#define PHY_TESTEN_DATA (0x0 << 16)
+#define PHY_TESTCLK (0x1 << 1)
+#define PHY_TESTCLR (0x1 << 0)
+#define THS_SETTLE_COUNTER_THRESHOLD 0x04
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+enum mipi_dphy_sy_pads {
+ MIPI_DPHY_SY_PAD_SINK = 0,
+ MIPI_DPHY_SY_PAD_SOURCE,
+ MIPI_DPHY_SY_PADS_NUM,
+};
+
+enum dphy_reg_id {
+ GRF_DPHY_RX0_TURNDISABLE = 0,
+ GRF_DPHY_RX0_FORCERXMODE,
+ GRF_DPHY_RX0_FORCETXSTOPMODE,
+ GRF_DPHY_RX0_ENABLE,
+ GRF_DPHY_RX0_TESTCLR,
+ GRF_DPHY_RX0_TESTCLK,
+ GRF_DPHY_RX0_TESTEN,
+ GRF_DPHY_RX0_TESTDIN,
+ GRF_DPHY_RX0_TURNREQUEST,
+ GRF_DPHY_RX0_TESTDOUT,
+ GRF_DPHY_TX0_TURNDISABLE,
+ GRF_DPHY_TX0_FORCERXMODE,
+ GRF_DPHY_TX0_FORCETXSTOPMODE,
+ GRF_DPHY_TX0_TURNREQUEST,
+ GRF_DPHY_TX1RX1_TURNDISABLE,
+ GRF_DPHY_TX1RX1_FORCERXMODE,
+ GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
+ GRF_DPHY_TX1RX1_ENABLE,
+ GRF_DPHY_TX1RX1_MASTERSLAVEZ,
+ GRF_DPHY_TX1RX1_BASEDIR,
+ GRF_DPHY_TX1RX1_ENABLECLK,
+ GRF_DPHY_TX1RX1_TURNREQUEST,
+ GRF_DPHY_RX1_SRC_SEL,
+ /* rk3288 only */
+ GRF_CON_DISABLE_ISP,
+ GRF_CON_ISP_DPHY_SEL,
+ GRF_DSI_CSI_TESTBUS_SEL,
+ GRF_DVP_V18SEL,
+ /* below is for rk3399 only */
+ GRF_DPHY_RX0_CLK_INV_SEL,
+ GRF_DPHY_RX1_CLK_INV_SEL,
+};
+
+struct dphy_reg {
+ u32 offset;
+ u32 mask;
+ u32 shift;
+};
+
+#define PHY_REG(_offset, _width, _shift) \
+ { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
+
+static const struct dphy_reg rk3399_grf_dphy_regs[] = {
+ [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
+ [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
+ [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
+ [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
+ [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
+ [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
+ [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
+ [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
+ [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
+ [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
+ [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
+ [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
+ [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
+ [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
+ [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
+ [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
+ [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
+ [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
+ [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
+ [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
+ [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
+ [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
+ [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
+ [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
+ [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
+};
+
+static const struct dphy_reg rk3288_grf_dphy_regs[] = {
+ [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
+ [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
+ [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
+ [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
+ [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
+ [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
+ [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
+ [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
+ [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
+ [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
+ [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
+ [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
+ [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
+ [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
+ [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
+ [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
+ [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
+ [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
+ [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
+ [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
+ [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
+ [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
+ [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
+ [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
+ [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
+ [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
+ [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
+};
+
+struct hsfreq_range {
+ u32 range_h;
+ u8 cfg_bit;
+};
+
+struct mipidphy_priv;
+
+struct dphy_drv_data {
+ const char * const *clks;
+ int num_clks;
+ const struct hsfreq_range *hsfreq_ranges;
+ int num_hsfreq_ranges;
+ const struct dphy_reg *regs;
+};
+
+struct sensor_async_subdev {
+ struct v4l2_async_subdev asd;
+ struct v4l2_mbus_config mbus;
+ int lanes;
+};
+
+#define MAX_DPHY_CLK 8
+#define MAX_DPHY_SENSORS 2
+
+struct mipidphy_sensor {
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_config mbus;
+ int lanes;
+};
+
+struct mipidphy_priv {
+ struct device *dev;
+ struct regmap *regmap_grf;
+ const struct dphy_reg *grf_regs;
+ struct clk *clks[MAX_DPHY_CLK];
+ const struct dphy_drv_data *drv_data;
+ u64 data_rate_mbps;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev sd;
+ struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
+ struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
+ int num_sensors;
+ bool is_streaming;
+ void __iomem *txrx_base_addr;
+ int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
+};
+
+static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct mipidphy_priv, sd);
+}
+
+static inline void write_grf_reg(struct mipidphy_priv *priv,
+ int index, u8 value)
+{
+ const struct dphy_reg *reg = &priv->grf_regs[index];
+ unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
+
+ WARN_ON(!reg->offset);
+ regmap_write(priv->regmap_grf, reg->offset, val);
+}
+
+static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
+ u8 test_code, u8 test_data)
+{
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current test code. Test data is
+ * programmed internally by rising edge on TESTCLK.
+ */
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+}
+
+static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
+ unsigned char data)
+{
+ /*
+ * TESTEN =1,TESTDIN=addr
+ * TESTCLK=0
+ * TESTEN =0,TESTDIN=data
+ * TESTCLK=1
+ */
+ writel((PHY_TESTEN_ADDR | addr),
+ priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
+ writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ writel((PHY_TESTEN_DATA | data),
+ priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+}
+
+static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
+{
+ struct media_pad *local, *remote;
+ struct media_entity *sensor_me;
+
+ local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
+ remote = media_entity_remote_pad(local);
+ if (!remote) {
+ v4l2_warn(sd, "No link between dphy and sensor\n");
+ return NULL;
+ }
+
+ sensor_me = media_entity_remote_pad(local)->entity;
+ return media_entity_to_v4l2_subdev(sensor_me);
+}
+
+static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ int i;
+
+ for (i = 0; i < priv->num_sensors; ++i)
+ if (priv->sensors[i].sd == sd)
+ return &priv->sensors[i];
+
+ return NULL;
+}
+
+static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
+ int ret;
+
+ link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
+ if (!link_freq) {
+ v4l2_warn(sd, "No pixel rate control in subdev\n");
+ return -EPIPE;
+ }
+
+ qm.index = v4l2_ctrl_g_ctrl(link_freq);
+ ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
+ if (ret < 0) {
+ v4l2_err(sd, "Failed to get menu item\n");
+ return ret;
+ }
+
+ if (!qm.value) {
+ v4l2_err(sd, "Invalid link_freq\n");
+ return -EINVAL;
+ }
+ priv->data_rate_mbps = qm.value * 2;
+ do_div(priv->data_rate_mbps, 1000 * 1000);
+
+ return 0;
+}
+
+static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int ret = 0;
+
+ if (priv->is_streaming)
+ return 0;
+
+ ret = mipidphy_get_sensor_data_rate(sd);
+ if (ret < 0)
+ return ret;
+
+ priv->stream_on(priv, sd);
+
+ priv->is_streaming = true;
+
+ return 0;
+}
+
+static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+
+ priv->is_streaming = false;
+
+ return 0;
+}
+
+static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
+{
+ if (on)
+ return mipidphy_s_stream_start(sd);
+ else
+ return mipidphy_s_stream_stop(sd);
+}
+
+static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *config)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+
+ *config = sensor->mbus;
+
+ return 0;
+}
+
+static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+
+ if (on)
+ return pm_runtime_get_sync(priv->dev);
+ else
+ return pm_runtime_put(priv->dev);
+}
+
+static int mipidphy_runtime_suspend(struct device *dev)
+{
+ struct media_entity *me = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int i, num_clks;
+
+ num_clks = priv->drv_data->num_clks;
+ for (i = num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(priv->clks[i]);
+
+ return 0;
+}
+
+static int mipidphy_runtime_resume(struct device *dev)
+{
+ struct media_entity *me = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int i, num_clks, ret;
+
+ num_clks = priv->drv_data->num_clks;
+ for (i = 0; i < num_clks; i++) {
+ ret = clk_prepare_enable(priv->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (--i >= 0)
+ clk_disable_unprepare(priv->clks[i]);
+ return ret;
+}
+
+/* dphy accepts all fmt/size from sensor */
+static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_subdev *sensor = get_remote_sensor(sd);
+
+ /*
+ * Do not allow format changes and just relay whatever
+ * set currently in the sensor.
+ */
+ return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
+}
+
+static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
+ .set_fmt = mipidphy_get_set_fmt,
+ .get_fmt = mipidphy_get_set_fmt,
+};
+
+static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
+ .s_power = mipidphy_s_power,
+};
+
+static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
+ .g_mbus_config = mipidphy_g_mbus_config,
+ .s_stream = mipidphy_s_stream,
+};
+
+static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
+ .core = &mipidphy_core_ops,
+ .video = &mipidphy_video_ops,
+ .pad = &mipidphy_subdev_pad_ops,
+};
+
+/* These tables must be sorted by .range_h ascending. */
+static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
+ { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
+ { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
+ { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
+ { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
+ { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
+ { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
+ { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
+ { 999, 0x1a}
+};
+
+static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
+ { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
+ { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
+ { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
+ { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
+ { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
+ { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
+ { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
+ { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
+ {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
+ {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
+};
+
+static const char * const rk3399_mipidphy_clks[] = {
+ "dphy-ref",
+ "dphy-cfg",
+ "grf",
+};
+
+static const char * const rk3288_mipidphy_clks[] = {
+ "dphy-ref",
+ "pclk",
+};
+
+static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
+ int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
+ int i, hsfreq = 0;
+
+ for (i = 0; i < num_hsfreq_ranges; i++) {
+ if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
+ hsfreq = hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
+ /* Disable lan turn around, which is ignored in receive mode */
+ write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
+
+ write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
+
+ /* dphy start */
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
+ usleep_range(100, 150);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
+ usleep_range(100, 150);
+
+ /* set clock lane */
+ /* HS hsfreq_range & lane 0 settle bypass */
+ mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane0 */
+ mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
+ /* HS RX Control of lane1 */
+ mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane2 */
+ mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane3 */
+ mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
+ /* HS RX Data Lanes Settle State Time Control */
+ mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
+ THS_SETTLE_COUNTER_THRESHOLD);
+
+ /* Normal operation */
+ mipidphy0_wr_reg(priv, 0x0, 0);
+
+ return 0;
+}
+
+static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
+ int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
+ int i, hsfreq = 0;
+
+ for (i = 0; i < num_hsfreq_ranges; i++) {
+ if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
+ hsfreq = hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
+ write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
+ write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
+ /* Disable lan turn around, which is ignored in receive mode */
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
+ GENMASK(sensor->lanes - 1, 0));
+ /* dphy start */
+ writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
+ writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ usleep_range(100, 150);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ usleep_range(100, 150);
+
+ /* set clock lane */
+ mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
+ mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
+ /* HS RX Data Lanes Settle State Time Control */
+ mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
+ THS_SETTLE_COUNTER_THRESHOLD);
+
+ /* Normal operation */
+ mipidphy1_wr_reg(priv, 0x0, 0);
+
+ return 0;
+}
+
+static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
+ .clks = rk3288_mipidphy_clks,
+ .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
+ .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
+ .regs = rk3288_grf_dphy_regs,
+};
+
+static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
+ .clks = rk3399_mipidphy_clks,
+ .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
+ .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
+ .regs = rk3399_grf_dphy_regs,
+};
+
+static const struct of_device_id rockchip_mipidphy_match_id[] = {
+ {
+ .compatible = "rockchip,rk3399-mipi-dphy",
+ .data = &rk3399_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3288-mipi-dphy",
+ .data = &rk3288_mipidphy_drv_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
+
+/* The .bound() notifier callback when a match is found */
+static int
+rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct mipidphy_priv *priv = container_of(notifier,
+ struct mipidphy_priv,
+ notifier);
+ struct sensor_async_subdev *s_asd = container_of(asd,
+ struct sensor_async_subdev, asd);
+ struct mipidphy_sensor *sensor;
+ unsigned int pad, ret;
+
+ if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
+ return -EBUSY;
+
+ sensor = &priv->sensors[priv->num_sensors++];
+ sensor->lanes = s_asd->lanes;
+ sensor->mbus = s_asd->mbus;
+ sensor->sd = sd;
+
+ for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
+ if (sensor->sd->entity.pads[pad].flags
+ & MEDIA_PAD_FL_SOURCE)
+ break;
+
+ if (pad == sensor->sd->entity.num_pads) {
+ dev_err(priv->dev,
+ "failed to find src pad for %s\n",
+ sensor->sd->name);
+
+ return -ENXIO;
+ }
+
+ ret = media_create_pad_link(
+ &sensor->sd->entity, pad,
+ &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
+ priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to create link for %s\n",
+ sensor->sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* The .unbind callback */
+static void
+rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct mipidphy_priv *priv = container_of(notifier,
+ struct mipidphy_priv,
+ notifier);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
+
+ sensor->sd = NULL;
+}
+
+static const struct
+v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
+ .bound = rockchip_mipidphy_notifier_bound,
+ .unbind = rockchip_mipidphy_notifier_unbind,
+};
+
+static int rockchip_mipidphy_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct sensor_async_subdev *s_asd =
+ container_of(asd, struct sensor_async_subdev, asd);
+ struct v4l2_mbus_config *config = &s_asd->mbus;
+
+ if (vep->bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "Only CSI2 bus type is currently supported\n");
+ return -EINVAL;
+ }
+
+ if (vep->base.port != 0) {
+ dev_err(dev, "The PHY has only port 0\n");
+ return -EINVAL;
+ }
+
+ config->type = V4L2_MBUS_CSI2;
+ config->flags = vep->bus.mipi_csi2.flags;
+ s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
+
+ switch (vep->bus.mipi_csi2.num_data_lanes) {
+ case 1:
+ config->flags |= V4L2_MBUS_CSI2_1_LANE;
+ break;
+ case 2:
+ config->flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case 3:
+ config->flags |= V4L2_MBUS_CSI2_3_LANE;
+ break;
+ case 4:
+ config->flags |= V4L2_MBUS_CSI2_4_LANE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
+{
+ int ret;
+
+ priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
+ MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
+ priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&priv->sd.entity,
+ MIPI_DPHY_SY_PADS_NUM, priv->pads);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
+ priv->dev, &priv->notifier,
+ sizeof(struct sensor_async_subdev), 0,
+ rockchip_mipidphy_fwnode_parse);
+ if (ret < 0)
+ return ret;
+
+ if (!priv->notifier.num_subdevs)
+ return -ENODEV; /* no endpoint */
+
+ priv->sd.subdev_notifier = &priv->notifier;
+ priv->notifier.ops = &rockchip_mipidphy_async_ops;
+ ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to register async notifier : %d\n", ret);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+ return ret;
+ }
+
+ return v4l2_async_register_subdev(&priv->sd);
+}
+
+static int rockchip_mipidphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd;
+ struct mipidphy_priv *priv;
+ struct regmap *grf;
+ struct resource *res;
+ const struct of_device_id *of_id;
+ const struct dphy_drv_data *drv_data;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+
+ of_id = of_match_device(rockchip_mipidphy_match_id, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ grf = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(grf)) {
+ grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(dev, "Can't find GRF syscon\n");
+ return -ENODEV;
+ }
+ }
+ priv->regmap_grf = grf;
+
+ drv_data = of_id->data;
+ for (i = 0; i < drv_data->num_clks; i++) {
+ priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
+
+ if (IS_ERR(priv->clks[i])) {
+ dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
+ return PTR_ERR(priv->clks[i]);
+ }
+ }
+
+ priv->grf_regs = drv_data->regs;
+ priv->drv_data = drv_data;
+ priv->stream_on = mipidphy_txrx_stream_on;
+ priv->txrx_base_addr = NULL;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->txrx_base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->txrx_base_addr))
+ priv->stream_on = mipidphy_rx_stream_on;
+
+ sd = &priv->sd;
+ v4l2_subdev_init(sd, &mipidphy_subdev_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
+ sd->dev = dev;
+
+ platform_set_drvdata(pdev, &sd->entity);
+
+ ret = rockchip_mipidphy_media_init(priv);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static int rockchip_mipidphy_remove(struct platform_device *pdev)
+{
+ struct media_entity *me = platform_get_drvdata(pdev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+
+ media_entity_cleanup(&sd->entity);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
+ mipidphy_runtime_resume, NULL)
+};
+
+static struct platform_driver rockchip_isp_mipidphy_driver = {
+ .probe = rockchip_mipidphy_probe,
+ .remove = rockchip_mipidphy_remove,
+ .driver = {
+ .name = "rockchip-sy-mipi-dphy",
+ .pm = &rockchip_mipidphy_pm_ops,
+ .of_match_table = rockchip_mipidphy_match_id,
+ },
+};
+
+module_platform_driver(rockchip_isp_mipidphy_driver);
+MODULE_AUTHOR("Rockchip Camera/ISP team");
+MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
new file mode 100644
index 000000000000..c558791064a2
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef __MIPI_DPHY_SY_H__
+#define __MIPI_DPHY_SY_H__
+
+#include <media/v4l2-subdev.h>
+
+void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
+
+#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (3 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 13:57 ` Hans Verkuil
2018-05-03 9:09 ` Baruch Siach
2018-03-08 9:47 ` [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver Jacob Chen
` (15 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the subdev driver for rockchip isp1.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/rkisp1.h | 105 +++
2 files changed, 1282 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.c b/drivers/media/platform/rockchip/isp1/rkisp1.c
new file mode 100644
index 000000000000..bb16c8118c16
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
@@ -0,0 +1,1177 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-event.h>
+
+#include "common.h"
+#include "regs.h"
+
+#define CIF_ISP_INPUT_W_MAX 4032
+#define CIF_ISP_INPUT_H_MAX 3024
+#define CIF_ISP_INPUT_W_MIN 32
+#define CIF_ISP_INPUT_H_MIN 32
+#define CIF_ISP_OUTPUT_W_MAX CIF_ISP_INPUT_W_MAX
+#define CIF_ISP_OUTPUT_H_MAX CIF_ISP_INPUT_H_MAX
+#define CIF_ISP_OUTPUT_W_MIN CIF_ISP_INPUT_W_MIN
+#define CIF_ISP_OUTPUT_H_MIN CIF_ISP_INPUT_H_MIN
+
+/*
+ * NOTE: MIPI controller and input MUX are also configured in this file,
+ * because ISP Subdev is not only describe ISP submodule(input size,format, output size, format),
+ * but also a virtual route device.
+ */
+
+/*
+ * There are many variables named with format/frame in below code,
+ * please see here for their meaning.
+ *
+ * Cropping regions of ISP
+ *
+ * +---------------------------------------------------------+
+ * | Sensor image |
+ * | +---------------------------------------------------+ |
+ * | | ISP_ACQ (for black level) | |
+ * | | in_frm | |
+ * | | +--------------------------------------------+ | |
+ * | | | ISP_OUT | | |
+ * | | | in_crop | | |
+ * | | | +---------------------------------+ | | |
+ * | | | | ISP_IS | | | |
+ * | | | | rkisp1_isp_subdev: out_crop | | | |
+ * | | | +---------------------------------+ | | |
+ * | | +--------------------------------------------+ | |
+ * | +---------------------------------------------------+ |
+ * +---------------------------------------------------------+
+ */
+
+static inline struct rkisp1_device *sd_to_isp_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev);
+}
+
+/* Get sensor by enabled media link */
+static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
+{
+ struct media_pad *local;
+ struct media_entity *sensor_me;
+
+ local = &sd->entity.pads[RKISP1_ISP_PAD_SINK];
+ sensor_me = media_entity_remote_pad(local)->entity;
+
+ return media_entity_to_v4l2_subdev(sensor_me);
+}
+
+static struct rkisp1_sensor_info *sd_to_sensor(struct rkisp1_device *dev,
+ struct v4l2_subdev *sd)
+{
+ int i;
+
+ for (i = 0; i < dev->num_sensors; ++i)
+ if (dev->sensors[i].sd == sd)
+ return &dev->sensors[i];
+
+ return NULL;
+}
+
+/**************** register operations ****************/
+
+/*
+ * Image Stabilization.
+ * This should only be called when configuring CIF
+ * or at the frame end interrupt
+ */
+static void rkisp1_config_ism(struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
+ u32 val;
+
+ writel(0, base + CIF_ISP_IS_RECENTER);
+ writel(0, base + CIF_ISP_IS_MAX_DX);
+ writel(0, base + CIF_ISP_IS_MAX_DY);
+ writel(0, base + CIF_ISP_IS_DISPLACE);
+ writel(out_crop->left, base + CIF_ISP_IS_H_OFFS);
+ writel(out_crop->top, base + CIF_ISP_IS_V_OFFS);
+ writel(out_crop->width, base + CIF_ISP_IS_H_SIZE);
+ writel(out_crop->height, base + CIF_ISP_IS_V_SIZE);
+
+ /* IS(Image Stabilization) is always on, working as output crop */
+ writel(1, base + CIF_ISP_IS_CTRL);
+ val = readl(base + CIF_ISP_CTRL);
+ val |= CIF_ISP_CTRL_ISP_CFG_UPD;
+ writel(val, base + CIF_ISP_CTRL);
+}
+
+/*
+ * configure isp blocks with input format, size......
+ */
+static int rkisp1_config_isp(struct rkisp1_device *dev)
+{
+ struct ispsd_in_fmt *in_fmt;
+ struct ispsd_out_fmt *out_fmt;
+ struct v4l2_mbus_framefmt *in_frm;
+ struct v4l2_rect *out_crop, *in_crop;
+ struct rkisp1_sensor_info *sensor;
+ void __iomem *base = dev->base_addr;
+ u32 isp_ctrl = 0;
+ u32 irq_mask = 0;
+ u32 signal = 0;
+ u32 acq_mult = 0;
+
+ sensor = dev->active_sensor;
+ in_frm = &dev->isp_sdev.in_frm;
+ in_fmt = &dev->isp_sdev.in_fmt;
+ out_fmt = &dev->isp_sdev.out_fmt;
+ out_crop = &dev->isp_sdev.out_crop;
+ in_crop = &dev->isp_sdev.in_crop;
+
+ if (in_fmt->fmt_type == FMT_BAYER) {
+ acq_mult = 1;
+ if (out_fmt->fmt_type == FMT_BAYER) {
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl =
+ CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
+ else
+ isp_ctrl =
+ CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
+ } else {
+ writel(CIF_ISP_DEMOSAIC_TH(0xc),
+ base + CIF_ISP_DEMOSAIC);
+
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
+ else
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
+ }
+ } else if (in_fmt->fmt_type == FMT_YUV) {
+ acq_mult = 2;
+ if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
+ } else {
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU656;
+ else
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
+
+ }
+
+ irq_mask |= CIF_ISP_DATA_LOSS;
+ }
+
+ /* Set up input acquisition properties */
+ if (sensor->mbus.type == V4L2_MBUS_BT656 ||
+ sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ if (sensor->mbus.flags &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ signal = CIF_ISP_ACQ_PROP_POS_EDGE;
+ }
+
+ if (sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ if (sensor->mbus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ signal |= CIF_ISP_ACQ_PROP_VSYNC_LOW;
+
+ if (sensor->mbus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ signal |= CIF_ISP_ACQ_PROP_HSYNC_LOW;
+ }
+
+ writel(isp_ctrl, base + CIF_ISP_CTRL);
+ writel(signal | in_fmt->yuv_seq |
+ CIF_ISP_ACQ_PROP_BAYER_PAT(in_fmt->bayer_pat) |
+ CIF_ISP_ACQ_PROP_FIELD_SEL_ALL, base + CIF_ISP_ACQ_PROP);
+ writel(0, base + CIF_ISP_ACQ_NR_FRAMES);
+
+ /* Acquisition Size */
+ writel(0, base + CIF_ISP_ACQ_H_OFFS);
+ writel(0, base + CIF_ISP_ACQ_V_OFFS);
+ writel(acq_mult * in_frm->width, base + CIF_ISP_ACQ_H_SIZE);
+ writel(in_frm->height, base + CIF_ISP_ACQ_V_SIZE);
+
+ /* ISP Out Area */
+ writel(in_crop->left, base + CIF_ISP_OUT_H_OFFS);
+ writel(in_crop->top, base + CIF_ISP_OUT_V_OFFS);
+ writel(in_crop->width, base + CIF_ISP_OUT_H_SIZE);
+ writel(in_crop->height, base + CIF_ISP_OUT_V_SIZE);
+
+ /* interrupt mask */
+ irq_mask |= CIF_ISP_FRAME | CIF_ISP_V_START | CIF_ISP_PIC_SIZE_ERROR |
+ CIF_ISP_FRAME_IN;
+ writel(irq_mask, base + CIF_ISP_IMSC);
+
+ if (out_fmt->fmt_type == FMT_BAYER)
+ rkisp1_params_disable_isp(&dev->params_vdev);
+ else
+ rkisp1_params_configure_isp(&dev->params_vdev, in_fmt,
+ dev->isp_sdev.quantization);
+
+ return 0;
+}
+
+static int rkisp1_config_dvp(struct rkisp1_device *dev)
+{
+ struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
+ void __iomem *base = dev->base_addr;
+ u32 val, input_sel;
+
+ switch (in_fmt->bus_width) {
+ case 8:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
+ break;
+ case 10:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
+ break;
+ case 12:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "Invalid bus width\n");
+ return -EINVAL;
+ }
+
+ val = readl(base + CIF_ISP_ACQ_PROP);
+ writel(val | input_sel, base + CIF_ISP_ACQ_PROP);
+
+ return 0;
+}
+
+static int rkisp1_config_mipi(struct rkisp1_device *dev)
+{
+ u32 mipi_ctrl;
+ void __iomem *base = dev->base_addr;
+ struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ int lanes;
+
+ /*
+ * sensor->mbus is set in isp or d-phy notifier_bound function
+ */
+ switch (sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) {
+ case V4L2_MBUS_CSI2_4_LANE:
+ lanes = 4;
+ break;
+ case V4L2_MBUS_CSI2_3_LANE:
+ lanes = 3;
+ break;
+ case V4L2_MBUS_CSI2_2_LANE:
+ lanes = 2;
+ break;
+ case V4L2_MBUS_CSI2_1_LANE:
+ lanes = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
+ CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
+ CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
+ CIF_MIPI_CTRL_CLOCKLANE_ENA;
+
+ writel(mipi_ctrl, base + CIF_MIPI_CTRL);
+
+ /* Configure Data Type and Virtual Channel */
+ writel(CIF_MIPI_DATA_SEL_DT(in_fmt->mipi_dt) | CIF_MIPI_DATA_SEL_VC(0),
+ base + CIF_MIPI_IMG_DATA_SEL);
+
+ /* Clear MIPI interrupts */
+ writel(~0, base + CIF_MIPI_ICR);
+ /*
+ * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for
+ * isp bus may be dead when switch isp.
+ */
+ writel(CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY |
+ CIF_MIPI_SYNC_FIFO_OVFLW(0x03) | CIF_MIPI_ADD_DATA_OVFLW,
+ base + CIF_MIPI_IMSC);
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "\n MIPI_CTRL 0x%08x\n"
+ " MIPI_IMG_DATA_SEL 0x%08x\n"
+ " MIPI_STATUS 0x%08x\n"
+ " MIPI_IMSC 0x%08x\n",
+ readl(base + CIF_MIPI_CTRL),
+ readl(base + CIF_MIPI_IMG_DATA_SEL),
+ readl(base + CIF_MIPI_STATUS),
+ readl(base + CIF_MIPI_IMSC));
+
+ return 0;
+}
+
+/* Configure MUX */
+static int rkisp1_config_path(struct rkisp1_device *dev)
+{
+ int ret = 0;
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ u32 dpcl = readl(dev->base_addr + CIF_VI_DPCL);
+
+ if (sensor->mbus.type == V4L2_MBUS_BT656 ||
+ sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ ret = rkisp1_config_dvp(dev);
+ dpcl |= CIF_VI_DPCL_IF_SEL_PARALLEL;
+ } else if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ ret = rkisp1_config_mipi(dev);
+ dpcl |= CIF_VI_DPCL_IF_SEL_MIPI;
+ }
+
+ writel(dpcl, dev->base_addr + CIF_VI_DPCL);
+
+ return ret;
+}
+
+/* Hareware configure Entry */
+static int rkisp1_config_cif(struct rkisp1_device *dev)
+{
+ int ret = 0;
+ u32 cif_id;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ cif_id = readl(dev->base_addr + CIF_VI_ID);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "CIF_ID 0x%08x\n", cif_id);
+
+ ret = rkisp1_config_isp(dev);
+ if (ret < 0)
+ return ret;
+ ret = rkisp1_config_path(dev);
+ if (ret < 0)
+ return ret;
+ rkisp1_config_ism(dev);
+
+ return 0;
+}
+
+/* Mess register operations to stop isp */
+static int rkisp1_isp_stop(struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ /*
+ * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
+ * Stop ISP(isp) ->wait for ISP isp off
+ */
+ /* stop and clear MI, MIPI, and ISP interrupts */
+ writel(0, base + CIF_MIPI_IMSC);
+ writel(~0, base + CIF_MIPI_ICR);
+
+ writel(0, base + CIF_ISP_IMSC);
+ writel(~0, base + CIF_ISP_ICR);
+
+ writel(0, base + CIF_MI_IMSC);
+ writel(~0, base + CIF_MI_ICR);
+ val = readl(base + CIF_MIPI_CTRL);
+ writel(val & (~CIF_MIPI_CTRL_OUTPUT_ENA), base + CIF_MIPI_CTRL);
+ /* stop ISP */
+ val = readl(base + CIF_ISP_CTRL);
+ val &= ~(CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE);
+ writel(val, base + CIF_ISP_CTRL);
+
+ val = readl(base + CIF_ISP_CTRL);
+ writel(val | CIF_ISP_CTRL_ISP_CFG_UPD, base + CIF_ISP_CTRL);
+
+ readx_poll_timeout(readl, base + CIF_ISP_RIS,
+ val, val & CIF_ISP_OFF, 20, 100);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "state(MP:%d, SP:%d), MI_CTRL:%x, ISP_CTRL:%x, MIPI_CTRL:%x\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state,
+ readl(base + CIF_MI_CTRL),
+ readl(base + CIF_ISP_CTRL),
+ readl(base + CIF_MIPI_CTRL));
+
+ writel(CIF_IRCL_MIPI_SW_RST | CIF_IRCL_ISP_SW_RST, base + CIF_IRCL);
+ writel(0x0, base + CIF_IRCL);
+
+ return 0;
+}
+
+/* Mess register operations to start isp */
+static int rkisp1_isp_start(struct rkisp1_device *dev)
+{
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ /* Activate MIPI */
+ if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ val = readl(base + CIF_MIPI_CTRL);
+ writel(val | CIF_MIPI_CTRL_OUTPUT_ENA, base + CIF_MIPI_CTRL);
+ }
+ /* Activate ISP */
+ val = readl(base + CIF_ISP_CTRL);
+ val |= CIF_ISP_CTRL_ISP_CFG_UPD | CIF_ISP_CTRL_ISP_ENABLE |
+ CIF_ISP_CTRL_ISP_INFORM_ENABLE;
+ writel(val, base + CIF_ISP_CTRL);
+
+ /* XXX: Is the 1000us too long?
+ * CIF spec says to wait for sufficient time after enabling
+ * the MIPI interface and before starting the sensor output.
+ */
+ usleep_range(1000, 1200);
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d MI_CTRL 0x%08x\n"
+ " ISP_CTRL 0x%08x MIPI_CTRL 0x%08x\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state,
+ readl(base + CIF_MI_CTRL),
+ readl(base + CIF_ISP_CTRL),
+ readl(base + CIF_MIPI_CTRL));
+
+ return 0;
+}
+
+static void rkisp1_config_clk(struct rkisp1_device *dev)
+{
+ u32 val = CIF_ICCL_ISP_CLK | CIF_ICCL_CP_CLK | CIF_ICCL_MRSZ_CLK |
+ CIF_ICCL_SRSZ_CLK | CIF_ICCL_JPEG_CLK | CIF_ICCL_MI_CLK |
+ CIF_ICCL_IE_CLK | CIF_ICCL_MIPI_CLK | CIF_ICCL_DCROP_CLK;
+
+ writel(val, dev->base_addr + CIF_ICCL);
+}
+
+/***************************** isp sub-devs *******************************/
+
+static const struct ispsd_in_fmt rkisp1_isp_input_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_YCBYCR,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_YCRYCB,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_CBYCRY,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_CRYCBY,
+ .bus_width = 16,
+ },
+};
+
+static const struct ispsd_out_fmt rkisp1_isp_output_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt_type = FMT_YUV,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt_type = FMT_BAYER,
+ },
+};
+
+static const struct ispsd_in_fmt *find_in_fmt(u32 mbus_code)
+{
+ const struct ispsd_in_fmt *fmt;
+ int i, array_size = ARRAY_SIZE(rkisp1_isp_input_formats);
+
+ for (i = 0; i < array_size; i++) {
+ fmt = &rkisp1_isp_input_formats[i];
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct ispsd_out_fmt *find_out_fmt(u32 mbus_code)
+{
+ const struct ispsd_out_fmt *fmt;
+ int i, array_size = ARRAY_SIZE(rkisp1_isp_output_formats);
+
+ for (i = 0; i < array_size; i++) {
+ fmt = &rkisp1_isp_output_formats[i];
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int rkisp1_isp_sd_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ int i = code->index;
+
+ if (code->pad == RKISP1_ISP_PAD_SINK) {
+ if (i >= ARRAY_SIZE(rkisp1_isp_input_formats))
+ return -EINVAL;
+ code->code = rkisp1_isp_input_formats[i].mbus_code;
+ } else {
+ if (i >= ARRAY_SIZE(rkisp1_isp_output_formats))
+ return -EINVAL;
+ code->code = rkisp1_isp_output_formats[i].mbus_code;
+ }
+
+ return 0;
+}
+
+#define sd_to_isp_sd(_sd) container_of(_sd, struct rkisp1_isp_subdev, sd)
+static int rkisp1_isp_sd_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+
+ if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
+ (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
+ return -EINVAL;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ if (fmt->pad == RKISP1_ISP_PAD_SINK) {
+ *mf = isp_sd->in_frm;
+ } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ /* format of source pad */
+ *mf = isp_sd->in_frm;
+ /* window size of source pad */
+ mf->width = isp_sd->out_crop.width;
+ mf->height = isp_sd->out_crop.height;
+ mf->quantization = isp_sd->quantization;
+ }
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static void rkisp1_isp_sd_try_fmt(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
+ const struct ispsd_in_fmt *in_fmt;
+ const struct ispsd_out_fmt *out_fmt;
+
+ switch (pad) {
+ case RKISP1_ISP_PAD_SINK:
+ in_fmt = find_in_fmt(fmt->code);
+ if (in_fmt)
+ fmt->code = in_fmt->mbus_code;
+ else
+ fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
+ fmt->width = clamp_t(u32, fmt->width, CIF_ISP_INPUT_W_MIN,
+ CIF_ISP_INPUT_W_MAX);
+ fmt->height = clamp_t(u32, fmt->height, CIF_ISP_INPUT_H_MIN,
+ CIF_ISP_INPUT_H_MAX);
+ break;
+ case RKISP1_ISP_PAD_SOURCE_PATH:
+ out_fmt = find_out_fmt(fmt->code);
+ if (out_fmt)
+ fmt->code = out_fmt->mbus_code;
+ else
+ fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ /* window size is set in s_selection */
+ fmt->width = isp_sd->out_crop.width;
+ fmt->height = isp_sd->out_crop.height;
+ /* full range by default */
+ if (!fmt->quantization)
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int rkisp1_isp_sd_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+ if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
+ (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
+ return -EINVAL;
+
+ rkisp1_isp_sd_try_fmt(sd, fmt->pad, mf);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *try_mf;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ *try_mf = *mf;
+ return 0;
+ }
+
+ if (fmt->pad == RKISP1_ISP_PAD_SINK) {
+ const struct ispsd_in_fmt *in_fmt;
+
+ in_fmt = find_in_fmt(mf->code);
+ isp_sd->in_fmt = *in_fmt;
+ isp_sd->in_frm = *mf;
+ } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ const struct ispsd_out_fmt *out_fmt;
+
+ /* Ignore width/height */
+ out_fmt = find_out_fmt(mf->code);
+ isp_sd->out_fmt = *out_fmt;
+ /*
+ * It is quantization for output,
+ * isp use bt601 limit-range in internal
+ */
+ isp_sd->quantization = mf->quantization;
+ }
+
+ return 0;
+}
+
+static void rkisp1_isp_sd_try_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+ struct v4l2_mbus_framefmt in_frm = isp_sd->in_frm;
+ struct v4l2_rect in_crop = isp_sd->in_crop;
+ struct v4l2_rect *input = &sel->r;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ in_frm = *v4l2_subdev_get_try_format(sd, cfg, RKISP1_ISP_PAD_SINK);
+ in_crop = *v4l2_subdev_get_try_crop(sd, cfg, RKISP1_ISP_PAD_SINK);
+ }
+
+ input->left = ALIGN(input->left, 2);
+ input->width = ALIGN(input->width, 2);
+
+ if (sel->pad == RKISP1_ISP_PAD_SINK) {
+ input->left = clamp_t(u32, input->left, 0, in_frm.width);
+ input->top = clamp_t(u32, input->top, 0, in_frm.height);
+ input->width = clamp_t(u32, input->width, CIF_ISP_INPUT_W_MIN,
+ in_frm.width - input->left);
+ input->height = clamp_t(u32, input->height,
+ CIF_ISP_INPUT_H_MIN,
+ in_frm.height - input->top);
+ } else if (sel->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ input->left = clamp_t(u32, input->left, 0, in_crop.width);
+ input->top = clamp_t(u32, input->top, 0, in_crop.height);
+ input->width = clamp_t(u32, input->width, CIF_ISP_OUTPUT_W_MIN,
+ in_crop.width - input->left);
+ input->height = clamp_t(u32, input->height, CIF_ISP_OUTPUT_H_MIN,
+ in_crop.height - input->top);
+ }
+}
+
+static int rkisp1_isp_sd_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+
+ if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
+ sel->pad != RKISP1_ISP_PAD_SINK)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (sel->pad == RKISP1_ISP_PAD_SINK) {
+ sel->r.height = isp_sd->in_frm.height;
+ sel->r.width = isp_sd->in_frm.width;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ } else {
+ sel->r = isp_sd->in_crop;
+ }
+ break;
+ case V4L2_SEL_TGT_CROP:
+ if (sel->pad == RKISP1_ISP_PAD_SINK)
+ sel->r = isp_sd->in_crop;
+ else
+ sel->r = isp_sd->out_crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *try_sel;
+
+ try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
+ sel->r = *try_sel;
+ }
+
+ return 0;
+}
+
+static int rkisp1_isp_sd_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+ struct rkisp1_device *dev = sd_to_isp_dev(sd);
+
+ if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
+ sel->pad != RKISP1_ISP_PAD_SINK)
+ return -EINVAL;
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+ rkisp1_isp_sd_try_crop(sd, cfg, sel);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *try_sel;
+
+ try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
+ *try_sel = sel->r;
+ return 0;
+ }
+
+ if (sel->pad == RKISP1_ISP_PAD_SINK)
+ isp_sd->in_crop = sel->r;
+ else
+ isp_sd->out_crop = sel->r;
+
+ return 0;
+}
+
+static int rkisp1_isp_sd_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_sensor_info *sensor;
+ struct v4l2_subdev *sensor_sd;
+ int ret = 0;
+
+ if (!on)
+ return rkisp1_isp_stop(isp_dev);
+
+ sensor_sd = get_remote_sensor(sd);
+ if (!sensor_sd)
+ return -ENODEV;
+
+ sensor = sd_to_sensor(isp_dev, sensor_sd);
+ /*
+ * Update sensor bus configuration. This is only effective
+ * for sensors chained off an external CSI2 PHY.
+ */
+ ret = v4l2_subdev_call(sensor->sd, video, g_mbus_config,
+ &sensor->mbus);
+ if (ret && ret != -ENOIOCTLCMD)
+ return ret;
+ isp_dev->active_sensor = sensor;
+
+ atomic_set(&isp_dev->isp_sdev.frm_sync_seq, 0);
+ ret = rkisp1_config_cif(isp_dev);
+ if (ret < 0)
+ return ret;
+
+ return rkisp1_isp_start(isp_dev);
+}
+
+static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ int ret;
+
+ v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
+
+ if (on) {
+ ret = pm_runtime_get_sync(isp_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ rkisp1_config_clk(isp_dev);
+ } else {
+ ret = pm_runtime_put(isp_dev->dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rkisp1_subdev_link_validate(struct media_link *link)
+{
+ if (link->source->index == RKISP1_ISP_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+static int rkisp1_subdev_fmt_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ if (source_fmt->format.code != sink_fmt->format.code)
+ return -EINVAL;
+
+ /* Crop is available */
+ if (source_fmt->format.width < sink_fmt->format.width ||
+ source_fmt->format.height < sink_fmt->format.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void
+riksp1_isp_queue_event_sof(struct rkisp1_isp_subdev *isp)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence =
+ atomic_inc_return(&isp->frm_sync_seq) - 1,
+ };
+ v4l2_event_queue(isp->sd.devnode, &event);
+}
+
+static int rkisp1_isp_sd_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* Line number. For now only zero accepted. */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static const struct v4l2_subdev_pad_ops rkisp1_isp_sd_pad_ops = {
+ .enum_mbus_code = rkisp1_isp_sd_enum_mbus_code,
+ .get_selection = rkisp1_isp_sd_get_selection,
+ .set_selection = rkisp1_isp_sd_set_selection,
+ .get_fmt = rkisp1_isp_sd_get_fmt,
+ .set_fmt = rkisp1_isp_sd_set_fmt,
+ .link_validate = rkisp1_subdev_fmt_link_validate,
+};
+
+static const struct media_entity_operations rkisp1_isp_sd_media_ops = {
+ .link_validate = rkisp1_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops rkisp1_isp_sd_video_ops = {
+ .s_stream = rkisp1_isp_sd_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
+ .subscribe_event = rkisp1_isp_sd_subs_evt,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ .s_power = rkisp1_isp_sd_s_power,
+};
+
+static struct v4l2_subdev_ops rkisp1_isp_sd_ops = {
+ .core = &rkisp1_isp_core_ops,
+ .video = &rkisp1_isp_sd_video_ops,
+ .pad = &rkisp1_isp_sd_pad_ops,
+};
+
+static void rkisp1_isp_sd_init_default_fmt(struct rkisp1_isp_subdev *isp_sd)
+{
+ struct v4l2_mbus_framefmt *in_frm = &isp_sd->in_frm;
+ struct v4l2_rect *in_crop = &isp_sd->in_crop;
+ struct v4l2_rect *out_crop = &isp_sd->out_crop;
+ struct ispsd_in_fmt *in_fmt = &isp_sd->in_fmt;
+ struct ispsd_out_fmt *out_fmt = &isp_sd->out_fmt;
+
+ *in_fmt = rkisp1_isp_input_formats[0];
+ in_frm->width = RKISP1_DEFAULT_WIDTH;
+ in_frm->height = RKISP1_DEFAULT_HEIGHT;
+ in_frm->code = in_fmt->mbus_code;
+
+ in_crop->width = in_frm->width;
+ in_crop->height = in_frm->height;
+ in_crop->left = 0;
+ in_crop->top = 0;
+
+ /* propagate to source */
+ *out_crop = *in_crop;
+ *out_fmt = rkisp1_isp_output_formats[0];
+}
+
+int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
+ struct v4l2_device *v4l2_dev)
+{
+ struct rkisp1_isp_subdev *isp_sdev = &isp_dev->isp_sdev;
+ struct v4l2_subdev *sd = &isp_sdev->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &rkisp1_isp_sd_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.ops = &rkisp1_isp_sd_media_ops;
+ snprintf(sd->name, sizeof(sd->name), "rkisp1-isp-subdev");
+
+ isp_sdev->pads[RKISP1_ISP_PAD_SINK].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ isp_sdev->pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_PATH].flags = MEDIA_PAD_FL_SOURCE;
+ isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+ ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX,
+ isp_sdev->pads);
+ if (ret < 0)
+ return ret;
+
+ sd->owner = THIS_MODULE;
+ v4l2_set_subdevdata(sd, isp_dev);
+
+ sd->grp_id = GRP_ID_ISP;
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret < 0) {
+ v4l2_err(sd, "Failed to register isp subdev\n");
+ goto err_cleanup_media_entity;
+ }
+
+ rkisp1_isp_sd_init_default_fmt(isp_sdev);
+
+ return 0;
+err_cleanup_media_entity:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev)
+{
+ struct v4l2_subdev *sd = &isp_dev->isp_sdev.sd;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+}
+
+/**************** Interrupter Handler ****************/
+
+void rkisp1_mipi_isr(unsigned int mis, struct rkisp1_device *dev)
+{
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ writel(~0, base + CIF_MIPI_ICR);
+
+ /*
+ * Disable DPHY errctrl interrupt, because this dphy
+ * erctrl signal is asserted until the next changes
+ * of line state. This time is may be too long and cpu
+ * is hold in this interrupt.
+ */
+ if (mis & CIF_MIPI_ERR_CTRL(0x0f)) {
+ val = readl(base + CIF_MIPI_IMSC);
+ writel(val & ~CIF_MIPI_ERR_CTRL(0x0f), base + CIF_MIPI_IMSC);
+ dev->isp_sdev.dphy_errctrl_disabled = true;
+ }
+
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (mis == CIF_MIPI_FRAME_END) {
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (dev->isp_sdev.dphy_errctrl_disabled) {
+ val = readl(base + CIF_MIPI_IMSC);
+ val |= CIF_MIPI_ERR_CTRL(0x0f);
+ writel(val, base + CIF_MIPI_IMSC);
+ dev->isp_sdev.dphy_errctrl_disabled = false;
+ }
+ } else {
+ v4l2_warn(v4l2_dev, "MIPI mis error: 0x%08x\n", mis);
+ }
+}
+
+void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ unsigned int isp_mis_tmp = 0;
+ unsigned int isp_err = 0;
+
+ /* start edge of v_sync */
+ if (isp_mis & CIF_ISP_V_START) {
+ riksp1_isp_queue_event_sof(&dev->isp_sdev);
+
+ writel(CIF_ISP_V_START, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_V_START)
+ v4l2_err(&dev->v4l2_dev, "isp icr v_statr err: 0x%x\n",
+ isp_mis_tmp);
+ }
+
+ if ((isp_mis & CIF_ISP_PIC_SIZE_ERROR)) {
+ /* Clear pic_size_error */
+ writel(CIF_ISP_PIC_SIZE_ERROR, base + CIF_ISP_ICR);
+ isp_err = readl(base + CIF_ISP_ERR);
+ v4l2_err(&dev->v4l2_dev,
+ "CIF_ISP_PIC_SIZE_ERROR (0x%08x)", isp_err);
+ writel(isp_err, base + CIF_ISP_ERR_CLR);
+ } else if ((isp_mis & CIF_ISP_DATA_LOSS)) {
+ /* Clear data_loss */
+ writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
+ v4l2_err(&dev->v4l2_dev, "CIF_ISP_DATA_LOSS\n");
+ writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
+ }
+
+ /* sampled input frame is complete */
+ if (isp_mis & CIF_ISP_FRAME_IN) {
+ writel(CIF_ISP_FRAME_IN, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_FRAME_IN)
+ v4l2_err(&dev->v4l2_dev, "isp icr frame_in err: 0x%x\n",
+ isp_mis_tmp);
+ }
+
+ /* frame was completely put out */
+ if (isp_mis & CIF_ISP_FRAME) {
+ u32 isp_ris = 0;
+ /* Clear Frame In (ISP) */
+ writel(CIF_ISP_FRAME, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_FRAME)
+ v4l2_err(&dev->v4l2_dev,
+ "isp icr frame end err: 0x%x\n", isp_mis_tmp);
+
+ isp_ris = readl(base + CIF_ISP_RIS);
+ if (isp_ris & (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN |
+ CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY))
+ rkisp1_stats_isr(&dev->stats_vdev, isp_ris);
+ }
+
+ /*
+ * Then update changed configs. Some of them involve
+ * lot of register writes. Do those only one per frame.
+ * Do the updates in the order of the processing flow.
+ */
+ rkisp1_params_isr(&dev->params_vdev, isp_mis);
+}
diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.h b/drivers/media/platform/rockchip/isp1/rkisp1.h
new file mode 100644
index 000000000000..f0bbcbf0c827
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.h
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_H
+#define _RKISP1_H
+
+#include <linux/platform_device.h>
+#include <media/v4l2-fwnode.h>
+#include "common.h"
+
+struct rkisp1_stream;
+
+/*
+ * struct ispsd_in_fmt - ISP intput-pad format
+ *
+ * Translate mbus_code to hardware format values
+ *
+ * @bus_width: used for parallel
+ */
+struct ispsd_in_fmt {
+ u32 mbus_code;
+ u8 fmt_type;
+ u32 mipi_dt;
+ u32 yuv_seq;
+ enum rkisp1_fmt_raw_pat_type bayer_pat;
+ u8 bus_width;
+};
+
+struct ispsd_out_fmt {
+ u32 mbus_code;
+ u8 fmt_type;
+};
+
+struct rkisp1_ie_config {
+ unsigned int effect;
+};
+
+enum rkisp1_isp_pad {
+ RKISP1_ISP_PAD_SINK,
+ RKISP1_ISP_PAD_SINK_PARAMS,
+ RKISP1_ISP_PAD_SOURCE_PATH,
+ RKISP1_ISP_PAD_SOURCE_STATS,
+ RKISP1_ISP_PAD_MAX
+};
+
+/*
+ * struct rkisp1_isp_subdev - ISP sub-device
+ *
+ * See Cropping regions of ISP in rkisp1.c for details
+ * @in_frm: input size, don't have to be equal to sensor size
+ * @in_fmt: intput format
+ * @in_crop: crop for sink pad
+ * @out_fmt: output format
+ * @out_crop: output size
+ *
+ * @dphy_errctrl_disabled: if dphy errctrl is disabled(avoid endless interrupt)
+ * @frm_sync_seq: frame sequence, to sync frame_id between video devices.
+ * @quantization: output quantization
+ */
+struct rkisp1_isp_subdev {
+ struct v4l2_subdev sd;
+ struct media_pad pads[RKISP1_ISP_PAD_MAX];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_mbus_framefmt in_frm;
+ struct ispsd_in_fmt in_fmt;
+ struct v4l2_rect in_crop;
+ struct ispsd_out_fmt out_fmt;
+ struct v4l2_rect out_crop;
+ bool dphy_errctrl_disabled;
+ atomic_t frm_sync_seq;
+ enum v4l2_quantization quantization;
+};
+
+int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
+ struct v4l2_device *v4l2_dev);
+
+void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev);
+
+void rkisp1_mipi_isr(unsigned int mipi_mis, struct rkisp1_device *dev);
+
+void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev);
+
+static inline
+struct ispsd_out_fmt *rkisp1_get_ispsd_out_fmt(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->out_fmt;
+}
+
+static inline
+struct ispsd_in_fmt *rkisp1_get_ispsd_in_fmt(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->in_fmt;
+}
+
+static inline
+struct v4l2_rect *rkisp1_get_isp_sd_win(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->out_crop;
+}
+
+#endif /* _RKISP1_H */
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (4 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 13:59 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 07/17] media: rkisp1: add ISP1 params driver Jacob Chen
` (14 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the capture video driver for rockchip isp1 statistics block.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/isp_stats.c | 508 +++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +++
2 files changed, 566 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
diff --git a/drivers/media/platform/rockchip/isp1/isp_stats.c b/drivers/media/platform/rockchip/isp1/isp_stats.c
new file mode 100644
index 000000000000..3ba86ccbc42d
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_stats.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP statistics */
+#include "dev.h"
+#include "regs.h"
+
+#define RKISP1_ISP_STATS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_STATS_REQ_BUFS_MAX 8
+
+static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_stats_vdev *stats_vdev = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = stats_vdev->vdev_fmt.fmt.meta.dataformat;
+ return 0;
+}
+
+static int rkisp1_stats_g_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_stats_vdev *stats_vdev = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = stats_vdev->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = stats_vdev->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_stats_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_stats_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = rkisp1_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_querycap = rkisp1_stats_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+struct v4l2_file_operations rkisp1_stats_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = vq->drv_priv;
+
+ *num_planes = 1;
+
+ *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_STATS_REQ_BUFS_MIN,
+ RKISP1_ISP_STATS_REQ_BUFS_MAX);
+
+ sizes[0] = sizeof(struct rkisp1_stat_buffer);
+
+ INIT_LIST_HEAD(&stats_vdev->stat);
+
+ return 0;
+}
+
+static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *stats_buf = to_rkisp1_buffer(vbuf);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_isp_stats_vdev *stats_dev = vq->drv_priv;
+ unsigned long flags;
+
+ stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
+ spin_lock_irqsave(&stats_dev->irq_lock, flags);
+ list_add_tail(&stats_buf->queue, &stats_dev->stat);
+ spin_unlock_irqrestore(&stats_dev->irq_lock, flags);
+}
+
+static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_stat_buffer))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_stat_buffer));
+
+ return 0;
+}
+
+static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ /* stop stats received firstly */
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ stats_vdev->streamon = false;
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+
+ drain_workqueue(stats_vdev->readout_wq);
+
+ for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) {
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ if (!list_empty(&stats_vdev->stat)) {
+ buf = list_first_entry(&stats_vdev->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+ break;
+ }
+
+ if (buf)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ buf = NULL;
+ }
+}
+
+static int
+rkisp1_stats_vb2_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = queue->drv_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ stats_vdev->streamon = true;
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops rkisp1_stats_vb2_ops = {
+ .queue_setup = rkisp1_stats_vb2_queue_setup,
+ .buf_queue = rkisp1_stats_vb2_buf_queue,
+ .buf_prepare = rkisp1_stats_vb2_buf_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = rkisp1_stats_vb2_stop_streaming,
+ .start_streaming = rkisp1_stats_vb2_start_streaming,
+};
+
+static int rkisp1_stats_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = queue_to_node(q);
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = stats_vdev;
+ q->ops = &rkisp1_stats_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_stats_get_awb_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ /* Protect against concurrent access from ISR? */
+ u32 reg_val;
+
+ pbuf->meas_type |= CIFISP_STAT_AWB;
+ reg_val = readl(stats_vdev->dev->base_addr + CIF_ISP_AWB_WHITE_CNT);
+ pbuf->params.awb.awb_mean[0].cnt = CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
+ reg_val = readl(stats_vdev->dev->base_addr + CIF_ISP_AWB_MEAN);
+
+ pbuf->params.awb.awb_mean[0].mean_cr_or_r =
+ CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_cb_or_b =
+ CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_y_or_g =
+ CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
+}
+
+static void rkisp1_stats_get_aec_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ unsigned int i;
+ void __iomem *addr = stats_vdev->dev->base_addr + CIF_ISP_EXP_MEAN_00;
+
+ pbuf->meas_type |= CIFISP_STAT_AUTOEXP;
+ for (i = 0; i < CIFISP_AE_MEAN_MAX; i++)
+ pbuf->params.ae.exp_mean[i] = (u8)readl(addr + i * 4);
+}
+
+static void rkisp1_stats_get_afc_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ void __iomem *base_addr;
+ struct cifisp_af_stat *af;
+
+ pbuf->meas_type = CIFISP_STAT_AFM_FIN;
+
+ af = &pbuf->params.af;
+ base_addr = stats_vdev->dev->base_addr;
+ af->window[0].sum = readl(base_addr + CIF_ISP_AFM_SUM_A);
+ af->window[0].lum = readl(base_addr + CIF_ISP_AFM_LUM_A);
+ af->window[1].sum = readl(base_addr + CIF_ISP_AFM_SUM_B);
+ af->window[1].lum = readl(base_addr + CIF_ISP_AFM_LUM_B);
+ af->window[2].sum = readl(base_addr + CIF_ISP_AFM_SUM_C);
+ af->window[2].lum = readl(base_addr + CIF_ISP_AFM_LUM_C);
+}
+
+static void rkisp1_stats_get_hst_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ int i;
+ void __iomem *addr = stats_vdev->dev->base_addr + CIF_ISP_HIST_BIN_0;
+
+ pbuf->meas_type |= CIFISP_STAT_HIST;
+ for (i = 0; i < CIFISP_HIST_BIN_N_MAX; i++)
+ pbuf->params.hist.hist_bins[i] = readl(addr + (i * 4));
+}
+
+static void rkisp1_stats_get_bls_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *dev = stats_vdev->dev;
+ const struct ispsd_in_fmt *in_fmt =
+ rkisp1_get_ispsd_in_fmt(&dev->isp_sdev);
+ void __iomem *base = stats_vdev->dev->base_addr;
+ struct cifisp_bls_meas_val *bls_val;
+
+ bls_val = &pbuf->params.ae.bls_val;
+ if (in_fmt->bayer_pat == RAW_BGGR) {
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_GBRG) {
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_GRBG) {
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_RGGB) {
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_D_MEASURED);
+ }
+}
+
+static void
+rkisp1_stats_send_measurement(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_isp_readout_work *meas_work)
+{
+ unsigned long lock_flags = 0;
+ unsigned int cur_frame_id = -1;
+ struct rkisp1_stat_buffer *cur_stat_buf;
+ struct rkisp1_buffer *cur_buf = NULL;
+
+ spin_lock_irqsave(&stats_vdev->irq_lock, lock_flags);
+ cur_frame_id = atomic_read(&stats_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+ if (cur_frame_id != meas_work->frame_id) {
+ v4l2_warn(stats_vdev->vnode.vdev.v4l2_dev,
+ "Measurement late(%d, %d)\n",
+ cur_frame_id, meas_work->frame_id);
+ cur_frame_id = meas_work->frame_id;
+ }
+ /* get one empty buffer */
+ if (!list_empty(&stats_vdev->stat)) {
+ cur_buf = list_first_entry(&stats_vdev->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&cur_buf->queue);
+ }
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, lock_flags);
+
+ if (!cur_buf)
+ return;
+
+ cur_stat_buf =
+ (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]);
+
+ if (meas_work->isp_ris & CIF_ISP_AWB_DONE) {
+ rkisp1_stats_get_awb_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AWB;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_AFM_FIN) {
+ rkisp1_stats_get_afc_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AFM_FIN;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_EXP_END) {
+ rkisp1_stats_get_aec_meas(stats_vdev, cur_stat_buf);
+ rkisp1_stats_get_bls_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AUTOEXP;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_HIST_MEASURE_RDY) {
+ rkisp1_stats_get_hst_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_HIST;
+ }
+
+ vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
+ sizeof(struct rkisp1_stat_buffer));
+ cur_buf->vb.sequence = cur_frame_id;
+ cur_buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void rkisp1_stats_readout_work(struct work_struct *work)
+{
+ struct rkisp1_isp_readout_work *readout_work = container_of(work,
+ struct rkisp1_isp_readout_work,
+ work);
+ struct rkisp1_isp_stats_vdev *stats_vdev = readout_work->stats_vdev;
+
+ if (readout_work->readout == RKISP1_ISP_READOUT_MEAS)
+ rkisp1_stats_send_measurement(stats_vdev, readout_work);
+
+ kfree(readout_work);
+}
+
+int rkisp1_stats_isr(struct rkisp1_isp_stats_vdev *stats_vdev, u32 isp_ris)
+{
+ unsigned int isp_mis_tmp = 0;
+ struct rkisp1_isp_readout_work *work;
+ unsigned int cur_frame_id =
+ atomic_read(&stats_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+#ifdef LOG_ISR_EXE_TIME
+ ktime_t in_t = ktime_get();
+#endif
+
+ writel((CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN | CIF_ISP_EXP_END |
+ CIF_ISP_HIST_MEASURE_RDY),
+ stats_vdev->dev->base_addr + CIF_ISP_ICR);
+
+ isp_mis_tmp = readl(stats_vdev->dev->base_addr + CIF_ISP_MIS);
+ if (isp_mis_tmp &
+ (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN |
+ CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY))
+ v4l2_err(stats_vdev->vnode.vdev.v4l2_dev,
+ "isp icr 3A info err: 0x%x\n",
+ isp_mis_tmp);
+
+ if (isp_ris & (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN | CIF_ISP_EXP_END |
+ CIF_ISP_HIST_MEASURE_RDY)) {
+ work = (struct rkisp1_isp_readout_work *)
+ kzalloc(sizeof(struct rkisp1_isp_readout_work),
+ GFP_ATOMIC);
+ if (work) {
+ INIT_WORK(&work->work,
+ rkisp1_stats_readout_work);
+ work->readout = RKISP1_ISP_READOUT_MEAS;
+ work->stats_vdev = stats_vdev;
+ work->frame_id = cur_frame_id;
+ work->isp_ris = isp_ris;
+ if (!queue_work(stats_vdev->readout_wq,
+ &work->work))
+ kfree(work);
+ } else {
+ v4l2_err(stats_vdev->vnode.vdev.v4l2_dev,
+ "Could not allocate work\n");
+ }
+ }
+
+#ifdef LOG_ISR_EXE_TIME
+ if (isp_ris & (CIF_ISP_EXP_END | CIF_ISP_AWB_DONE |
+ CIF_ISP_FRAME | CIF_ISP_HIST_MEASURE_RDY)) {
+ unsigned int diff_us =
+ ktime_to_us(ktime_sub(ktime_get(), in_t));
+
+ if (diff_us > g_longest_isr_time)
+ g_longest_isr_time = diff_us;
+
+ v4l2_info(stats_vdev->vnode.vdev.v4l2_dev,
+ "isp_isr time %d %d\n", diff_us, g_longest_isr_time);
+ }
+#endif
+
+ return 0;
+}
+
+static void rkisp1_init_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ stats_vdev->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_STAT_3A;
+ stats_vdev->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_stat_buffer);
+}
+
+int rkisp1_register_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev)
+{
+ int ret;
+ struct rkisp1_vdev_node *node = &stats_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ stats_vdev->dev = dev;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(&stats_vdev->stat);
+ spin_lock_init(&stats_vdev->irq_lock);
+
+ strlcpy(vdev->name, "rkisp1-statistics", sizeof(vdev->name));
+
+ video_set_drvdata(vdev, stats_vdev);
+ vdev->ioctl_ops = &rkisp1_stats_ioctl;
+ vdev->fops = &rkisp1_stats_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ rkisp1_stats_init_vb2_queue(vdev->queue, stats_vdev);
+ rkisp1_init_stats_vdev(stats_vdev);
+ video_set_drvdata(vdev, stats_vdev);
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret < 0)
+ goto err_release_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(&vdev->dev,
+ "could not register Video for Linux device\n");
+ goto err_cleanup_media_entity;
+ }
+
+ stats_vdev->readout_wq =
+ alloc_workqueue("measurement_queue",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+
+ if (!stats_vdev->readout_wq) {
+ ret = -ENOMEM;
+ goto err_unreg_vdev;
+ }
+
+ return 0;
+err_unreg_vdev:
+ video_unregister_device(vdev);
+err_cleanup_media_entity:
+ media_entity_cleanup(&vdev->entity);
+err_release_queue:
+ vb2_queue_release(vdev->queue);
+ return ret;
+}
+
+void rkisp1_unregister_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ struct rkisp1_vdev_node *node = &stats_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ destroy_workqueue(stats_vdev->readout_wq);
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ vb2_queue_release(vdev->queue);
+}
diff --git a/drivers/media/platform/rockchip/isp1/isp_stats.h b/drivers/media/platform/rockchip/isp1/isp_stats.h
new file mode 100644
index 000000000000..75f832a3d968
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_stats.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_ISP_STATS_H
+#define _RKISP1_ISP_STATS_H
+
+#include <linux/rkisp1-config.h>
+#include "common.h"
+
+struct rkisp1_isp_stats_vdev;
+
+enum rkisp1_isp_readout_cmd {
+ RKISP1_ISP_READOUT_MEAS,
+ RKISP1_ISP_READOUT_META,
+};
+
+struct rkisp1_isp_readout_work {
+ struct work_struct work;
+ struct rkisp1_isp_stats_vdev *stats_vdev;
+
+ unsigned int frame_id;
+ unsigned int isp_ris;
+ enum rkisp1_isp_readout_cmd readout;
+ struct vb2_buffer *vb;
+};
+
+/*
+ * struct rkisp1_isp_stats_vdev - ISP Statistics device
+ *
+ * @irq_lock: buffer queue lock
+ * @stat: stats buffer list
+ * @readout_wq: workqueue for statistics information read
+ */
+struct rkisp1_isp_stats_vdev {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *dev;
+
+ spinlock_t irq_lock;
+ struct list_head stat;
+ struct v4l2_format vdev_fmt;
+ bool streamon;
+
+ struct workqueue_struct *readout_wq;
+};
+
+int rkisp1_stats_isr(struct rkisp1_isp_stats_vdev *stats_vdev, u32 isp_ris);
+
+int rkisp1_register_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev);
+
+void rkisp1_unregister_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev);
+
+#endif /* _RKISP1_ISP_STATS_H */
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 07/17] media: rkisp1: add ISP1 params driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (5 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 14:03 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver Jacob Chen
` (13 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the output video driver that accept params from userspace.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++++++
drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
2 files changed, 1588 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
diff --git a/drivers/media/platform/rockchip/isp1/isp_params.c b/drivers/media/platform/rockchip/isp1/isp_params.c
new file mode 100644
index 000000000000..747108e02836
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_params.c
@@ -0,0 +1,1539 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP params */
+#include "dev.h"
+#include "regs.h"
+
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8
+
+#define BLS_START_H_MAX_IS_VALID(val) ((val) < CIFISP_BLS_START_H_MAX)
+#define BLS_STOP_H_MAX_IS_VALID(val) ((val) < CIFISP_BLS_STOP_H_MAX)
+
+#define BLS_START_V_MAX_IS_VALID(val) ((val) < CIFISP_BLS_START_V_MAX)
+#define BLS_STOP_V_MAX_IS_VALID(val) ((val) < CIFISP_BLS_STOP_V_MAX)
+
+#define BLS_SAMPLE_MAX_IS_VALID(val) ((val) < CIFISP_BLS_SAMPLES_MAX)
+
+#define BLS_FIX_SUB_IS_VALID(val) \
+ ((val) > (s16) CIFISP_BLS_FIX_SUB_MIN && (val) < CIFISP_BLS_FIX_SUB_MAX)
+
+#define RKISP1_ISP_DPCC_LINE_THRESH(n) (CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_LINE_MAD_FAC(n) (CIF_ISP_DPCC_LINE_MAD_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_PG_FAC(n) (CIF_ISP_DPCC_PG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RND_THRESH(n) (CIF_ISP_DPCC_RND_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RG_FAC(n) (CIF_ISP_DPCC_RG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_CC_COEFF(n) (CIF_ISP_CC_COEFF_0 + (n) * 4)
+
+static inline void rkisp1_iowrite32(struct rkisp1_isp_params_vdev *params_vdev,
+ u32 value, u32 addr)
+{
+ iowrite32(value, params_vdev->dev->base_addr + addr);
+}
+
+static inline u32 rkisp1_ioread32(struct rkisp1_isp_params_vdev *params_vdev,
+ u32 addr)
+{
+ return ioread32(params_vdev->dev->base_addr + addr);
+}
+
+static inline void isp_param_set_bits(struct rkisp1_isp_params_vdev
+ *params_vdev,
+ u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_ioread32(params_vdev, reg);
+ rkisp1_iowrite32(params_vdev, val | bit_mask, reg);
+}
+
+static inline void isp_param_clear_bits(struct rkisp1_isp_params_vdev
+ *params_vdev,
+ u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_ioread32(params_vdev, reg);
+ rkisp1_iowrite32(params_vdev, val & ~bit_mask, reg);
+}
+
+/* ISP BP interface function */
+static void dpcc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpcc_config *arg)
+{
+ unsigned int i;
+
+ rkisp1_iowrite32(params_vdev, arg->mode, CIF_ISP_DPCC_MODE);
+ rkisp1_iowrite32(params_vdev, arg->output_mode,
+ CIF_ISP_DPCC_OUTPUT_MODE);
+ rkisp1_iowrite32(params_vdev, arg->set_use, CIF_ISP_DPCC_SET_USE);
+
+ rkisp1_iowrite32(params_vdev, arg->methods[0].method,
+ CIF_ISP_DPCC_METHODS_SET_1);
+ rkisp1_iowrite32(params_vdev, arg->methods[1].method,
+ CIF_ISP_DPCC_METHODS_SET_2);
+ rkisp1_iowrite32(params_vdev, arg->methods[2].method,
+ CIF_ISP_DPCC_METHODS_SET_3);
+ for (i = 0; i < CIFISP_DPCC_METHODS_MAX; i++) {
+ rkisp1_iowrite32(params_vdev, arg->methods[i].line_thresh,
+ RKISP1_ISP_DPCC_LINE_THRESH(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].line_mad_fac,
+ RKISP1_ISP_DPCC_LINE_MAD_FAC(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].pg_fac,
+ RKISP1_ISP_DPCC_PG_FAC(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].rnd_thresh,
+ RKISP1_ISP_DPCC_RND_THRESH(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].rg_fac,
+ RKISP1_ISP_DPCC_RG_FAC(i));
+ }
+
+ rkisp1_iowrite32(params_vdev, arg->rnd_offs, CIF_ISP_DPCC_RND_OFFS);
+ rkisp1_iowrite32(params_vdev, arg->ro_limits, CIF_ISP_DPCC_RO_LIMITS);
+}
+
+/* ISP black level subtraction interface function */
+static void bls_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_bls_config *arg)
+{
+ u32 new_control = 0;
+
+ /* fixed subtraction values */
+ if (!arg->enable_auto) {
+ const struct cifisp_bls_fixed_val *pval = &arg->fixed_val;
+
+ switch (params_vdev->raw_type) {
+ case RAW_BGGR:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_A_FIXED);
+ break;
+ case RAW_GBRG:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_B_FIXED);
+ break;
+ case RAW_GRBG:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_C_FIXED);
+ break;
+ case RAW_RGGB:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_D_FIXED);
+ break;
+ default:
+ break;
+ }
+
+ new_control = CIF_ISP_BLS_MODE_FIXED;
+ } else {
+ if (arg->en_windows & BIT(1)) {
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.h_offs,
+ CIF_ISP_BLS_H2_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.h_size,
+ CIF_ISP_BLS_H2_STOP);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.v_offs,
+ CIF_ISP_BLS_V2_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.v_size,
+ CIF_ISP_BLS_V2_STOP);
+ new_control |= CIF_ISP_BLS_WINDOW_2;
+ }
+
+ if (arg->en_windows & BIT(0)) {
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.h_offs,
+ CIF_ISP_BLS_H1_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.h_size,
+ CIF_ISP_BLS_H1_STOP);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.v_offs,
+ CIF_ISP_BLS_V1_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.v_size,
+ CIF_ISP_BLS_V1_STOP);
+ new_control |= CIF_ISP_BLS_WINDOW_1;
+ }
+
+ rkisp1_iowrite32(params_vdev, arg->bls_samples,
+ CIF_ISP_BLS_SAMPLES);
+
+ new_control |= CIF_ISP_BLS_MODE_MEASURED;
+ }
+ rkisp1_iowrite32(params_vdev, new_control, CIF_ISP_BLS_CTRL);
+}
+
+/* ISP LS correction interface function */
+static void
+__lsc_correct_matrix_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_lsc_config *pconfig)
+{
+ int i, j;
+ unsigned int isp_lsc_status, sram_addr, isp_lsc_table_sel;
+ unsigned int data;
+
+ isp_lsc_status = rkisp1_ioread32(params_vdev, CIF_ISP_LSC_STATUS);
+
+ /* CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
+ sram_addr = (isp_lsc_status & CIF_ISP_LSC_ACTIVE_TABLE) ?
+ CIF_ISP_LSC_TABLE_ADDRESS_0 :
+ CIF_ISP_LSC_TABLE_ADDRESS_153;
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_R_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_GR_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_GB_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_B_TABLE_ADDR);
+
+ /* program data tables (table size is 9 * 17 = 153) */
+ for (i = 0; i < ((CIF_ISP_LSC_SECTORS_MAX + 1) *
+ (CIF_ISP_LSC_SECTORS_MAX + 1));
+ i += CIF_ISP_LSC_SECTORS_MAX + 1) {
+ /*
+ * 17 sectors with 2 values in one DWORD = 9
+ * DWORDs (2nd value of last DWORD unused)
+ */
+ for (j = 0; j < (CIF_ISP_LSC_SECTORS_MAX + 1); j += 2) {
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->r_data_tbl[i + j],
+ pconfig->r_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_R_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->gr_data_tbl[i + j],
+ pconfig->gr_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_GR_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->gb_data_tbl[i + j],
+ pconfig->gb_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_GB_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->b_data_tbl[i + j],
+ pconfig->b_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_B_TABLE_DATA);
+ }
+ }
+
+ isp_lsc_table_sel = (isp_lsc_status & CIF_ISP_LSC_ACTIVE_TABLE) ?
+ CIF_ISP_LSC_TABLE_0 : CIF_ISP_LSC_TABLE_1;
+ rkisp1_iowrite32(params_vdev, isp_lsc_table_sel, CIF_ISP_LSC_TABLE_SEL);
+}
+
+static void lsc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_lsc_config *arg)
+{
+ int i;
+ u32 lsc_ctrl;
+ unsigned int data;
+
+ /* To config must be off , store the current status firstly */
+ lsc_ctrl = rkisp1_ioread32(params_vdev, CIF_ISP_LSC_CTRL);
+ isp_param_clear_bits(params_vdev, CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ __lsc_correct_matrix_config(params_vdev, arg);
+
+ for (i = 0; i < 4; i++) {
+ /* program x size tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
+ arg->x_size_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_XSIZE_01 + i * 4);
+
+ /* program x grad tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2],
+ arg->x_grad_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_XGRAD_01 + i * 4);
+
+ /* program y size tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
+ arg->y_size_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_YSIZE_01 + i * 4);
+
+ /* program y grad tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2],
+ arg->y_grad_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_YGRAD_01 + i * 4);
+ }
+
+ /* restore the bls ctrl status */
+ if (lsc_ctrl & CIF_ISP_LSC_CTRL_ENA) {
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ } else {
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ }
+}
+
+/* ISP Filtering function */
+static void flt_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_flt_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->thresh_bl0, CIF_ISP_FILT_THRESH_BL0);
+ rkisp1_iowrite32(params_vdev, arg->thresh_bl1, CIF_ISP_FILT_THRESH_BL1);
+ rkisp1_iowrite32(params_vdev, arg->thresh_sh0, CIF_ISP_FILT_THRESH_SH0);
+ rkisp1_iowrite32(params_vdev, arg->thresh_sh1, CIF_ISP_FILT_THRESH_SH1);
+ rkisp1_iowrite32(params_vdev, arg->fac_bl0, CIF_ISP_FILT_FAC_BL0);
+ rkisp1_iowrite32(params_vdev, arg->fac_bl1, CIF_ISP_FILT_FAC_BL1);
+ rkisp1_iowrite32(params_vdev, arg->fac_mid, CIF_ISP_FILT_FAC_MID);
+ rkisp1_iowrite32(params_vdev, arg->fac_sh0, CIF_ISP_FILT_FAC_SH0);
+ rkisp1_iowrite32(params_vdev, arg->fac_sh1, CIF_ISP_FILT_FAC_SH1);
+ rkisp1_iowrite32(params_vdev, arg->lum_weight, CIF_ISP_FILT_LUM_WEIGHT);
+
+ rkisp1_iowrite32(params_vdev, (arg->mode ? CIF_ISP_FLT_MODE_DNR : 0) |
+ CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) |
+ CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) |
+ CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1),
+ CIF_ISP_FILT_MODE);
+}
+
+/* ISP demosaic interface function */
+static int bdm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_bdm_config *arg)
+{
+ /* set demosaic threshold */
+ rkisp1_iowrite32(params_vdev, arg->demosaic_th, CIF_ISP_DEMOSAIC);
+ return 0;
+}
+
+/* ISP GAMMA correction interface function */
+static void sdg_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_sdg_config *arg)
+{
+ int i;
+
+ rkisp1_iowrite32(params_vdev,
+ arg->xa_pnts.gamma_dx0, CIF_ISP_GAMMA_DX_LO);
+ rkisp1_iowrite32(params_vdev,
+ arg->xa_pnts.gamma_dx1, CIF_ISP_GAMMA_DX_HI);
+
+ for (i = 0; i < CIFISP_DEGAMMA_CURVE_SIZE; i++) {
+ rkisp1_iowrite32(params_vdev, arg->curve_r.gamma_y[i],
+ CIF_ISP_GAMMA_R_Y0 + i * 4);
+ rkisp1_iowrite32(params_vdev, arg->curve_g.gamma_y[i],
+ CIF_ISP_GAMMA_G_Y0 + i * 4);
+ rkisp1_iowrite32(params_vdev, arg->curve_b.gamma_y[i],
+ CIF_ISP_GAMMA_B_Y0 + i * 4);
+ }
+}
+
+/* ISP GAMMA correction interface function */
+static void goc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_goc_config *arg)
+{
+ int i;
+
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ rkisp1_iowrite32(params_vdev, arg->mode, CIF_ISP_GAMMA_OUT_MODE);
+
+ for (i = 0; i < CIFISP_GAMMA_OUT_MAX_SAMPLES; i++)
+ rkisp1_iowrite32(params_vdev, arg->gamma_y[i],
+ CIF_ISP_GAMMA_OUT_Y_0 + i * 4);
+}
+
+/* ISP Cross Talk */
+static void ctk_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_ctk_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->coeff0, CIF_ISP_CT_COEFF_0);
+ rkisp1_iowrite32(params_vdev, arg->coeff1, CIF_ISP_CT_COEFF_1);
+ rkisp1_iowrite32(params_vdev, arg->coeff2, CIF_ISP_CT_COEFF_2);
+ rkisp1_iowrite32(params_vdev, arg->coeff3, CIF_ISP_CT_COEFF_3);
+ rkisp1_iowrite32(params_vdev, arg->coeff4, CIF_ISP_CT_COEFF_4);
+ rkisp1_iowrite32(params_vdev, arg->coeff5, CIF_ISP_CT_COEFF_5);
+ rkisp1_iowrite32(params_vdev, arg->coeff6, CIF_ISP_CT_COEFF_6);
+ rkisp1_iowrite32(params_vdev, arg->coeff7, CIF_ISP_CT_COEFF_7);
+ rkisp1_iowrite32(params_vdev, arg->coeff8, CIF_ISP_CT_COEFF_8);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_r, CIF_ISP_CT_OFFSET_R);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_g, CIF_ISP_CT_OFFSET_G);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_b, CIF_ISP_CT_OFFSET_B);
+}
+
+static void ctk_enable(struct rkisp1_isp_params_vdev *params_vdev, bool en)
+{
+ if (en)
+ return;
+
+ /* Write back the default values. */
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_0);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_1);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_2);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_3);
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_4);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_5);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_6);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_7);
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_8);
+
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_R);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_G);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_B);
+}
+
+/* ISP White Balance Mode */
+static void awb_meas_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_meas_config *arg)
+{
+ u32 reg_val = 0;
+ /* based on the mode,configure the awb module */
+ if (arg->awb_mode == CIFISP_AWB_MODE_YCBCR) {
+ /* Reference Cb and Cr */
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) |
+ arg->awb_ref_cb, CIF_ISP_AWB_REF);
+ /* Yc Threshold */
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_MAX_Y_SET(arg->max_y) |
+ CIF_ISP_AWB_MIN_Y_SET(arg->min_y) |
+ CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) |
+ arg->min_c, CIF_ISP_AWB_THRESH);
+ }
+
+ reg_val = rkisp1_ioread32(params_vdev, CIF_ISP_AWB_PROP);
+ if (arg->enable_ymax_cmp)
+ reg_val |= CIF_ISP_AWB_YMAX_CMP_EN;
+ else
+ reg_val &= ~CIF_ISP_AWB_YMAX_CMP_EN;
+ rkisp1_iowrite32(params_vdev, reg_val, CIF_ISP_AWB_PROP);
+
+ /* window offset */
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.v_offs, CIF_ISP_AWB_WND_V_OFFS);
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.h_offs, CIF_ISP_AWB_WND_H_OFFS);
+ /* AWB window size */
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.v_size, CIF_ISP_AWB_WND_V_SIZE);
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.h_size, CIF_ISP_AWB_WND_H_SIZE);
+ /* Number of frames */
+ rkisp1_iowrite32(params_vdev,
+ arg->frames, CIF_ISP_AWB_FRAMES);
+}
+
+static void awb_meas_enable(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_meas_config *arg, bool en)
+{
+ u32 reg_val = rkisp1_ioread32(params_vdev, CIF_ISP_AWB_PROP);
+
+ /* switch off */
+ reg_val &= CIF_ISP_AWB_MODE_MASK_NONE;
+
+ if (en) {
+ if (arg->awb_mode == CIFISP_AWB_MODE_RGB)
+ reg_val |= CIF_ISP_AWB_MODE_RGB_EN;
+ else
+ reg_val |= CIF_ISP_AWB_MODE_YCBCR_EN;
+
+ rkisp1_iowrite32(params_vdev, reg_val, CIF_ISP_AWB_PROP);
+
+ /* Measurements require AWB block be active. */
+ /* TODO: need to enable here ? awb_gain_enable has done this */
+ isp_param_set_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ } else {
+ rkisp1_iowrite32(params_vdev,
+ reg_val, CIF_ISP_AWB_PROP);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+}
+
+static void awb_gain_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_gain_config *arg)
+{
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) |
+ arg->gain_green_b, CIF_ISP_AWB_GAIN_G);
+
+ rkisp1_iowrite32(params_vdev, CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) |
+ arg->gain_blue, CIF_ISP_AWB_GAIN_RB);
+}
+
+static void aec_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_aec_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+
+ rkisp1_iowrite32(params_vdev,
+ ((arg->autostop) ? CIF_ISP_EXP_CTRL_AUTOSTOP : 0) |
+ ((arg->mode == CIFISP_EXP_MEASURING_MODE_1) ?
+ CIF_ISP_EXP_CTRL_MEASMODE_1 : 0), CIF_ISP_EXP_CTRL);
+
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.h_offs, CIF_ISP_EXP_H_OFFSET);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.v_offs, CIF_ISP_EXP_V_OFFSET);
+
+ block_hsize = arg->meas_window.h_size / CIF_ISP_EXP_COLUMN_NUM - 1;
+ block_vsize = arg->meas_window.v_size / CIF_ISP_EXP_ROW_NUM - 1;
+
+ rkisp1_iowrite32(params_vdev, CIF_ISP_EXP_H_SIZE_SET(block_hsize),
+ CIF_ISP_EXP_H_SIZE);
+ rkisp1_iowrite32(params_vdev, CIF_ISP_EXP_V_SIZE_SET(block_vsize),
+ CIF_ISP_EXP_V_SIZE);
+}
+
+static void cproc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_cproc_config *arg)
+{
+ struct cifisp_isp_other_cfg *cur_other_cfg = ¶ms_vdev->cur_params.others;
+ struct cifisp_ie_config *cur_ie_config = &cur_other_cfg->ie_config;
+ u32 effect = cur_ie_config->effect;
+ u32 quantization = params_vdev->quantization;
+
+ rkisp1_iowrite32(params_vdev, arg->contrast, CIF_C_PROC_CONTRAST);
+ rkisp1_iowrite32(params_vdev, arg->hue, CIF_C_PROC_HUE);
+ rkisp1_iowrite32(params_vdev, arg->sat, CIF_C_PROC_SATURATION);
+ rkisp1_iowrite32(params_vdev, arg->brightness, CIF_C_PROC_BRIGHTNESS);
+
+ if (quantization != V4L2_QUANTIZATION_FULL_RANGE ||
+ effect != V4L2_COLORFX_NONE) {
+ isp_param_clear_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_YOUT_FULL |
+ CIF_C_PROC_YIN_FULL |
+ CIF_C_PROC_COUT_FULL);
+ } else {
+ isp_param_set_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_YOUT_FULL |
+ CIF_C_PROC_YIN_FULL |
+ CIF_C_PROC_COUT_FULL);
+ }
+}
+
+static void hst_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_hst_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+ static const u32 hist_weight_regs[] = {
+ CIF_ISP_HIST_WEIGHT_00TO30, CIF_ISP_HIST_WEIGHT_40TO21,
+ CIF_ISP_HIST_WEIGHT_31TO12, CIF_ISP_HIST_WEIGHT_22TO03,
+ CIF_ISP_HIST_WEIGHT_13TO43, CIF_ISP_HIST_WEIGHT_04TO34,
+ CIF_ISP_HIST_WEIGHT_44,
+ };
+ int i;
+ const u8 *weight;
+
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_HIST_PREDIV_SET(arg->histogram_predivider),
+ CIF_ISP_HIST_PROP);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.h_offs,
+ CIF_ISP_HIST_H_OFFS);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.v_offs,
+ CIF_ISP_HIST_V_OFFS);
+
+ block_hsize = arg->meas_window.h_size / CIF_ISP_HIST_COLUMN_NUM - 1;
+ block_vsize = arg->meas_window.v_size / CIF_ISP_HIST_ROW_NUM - 1;
+
+ rkisp1_iowrite32(params_vdev, block_hsize, CIF_ISP_HIST_H_SIZE);
+ rkisp1_iowrite32(params_vdev, block_vsize, CIF_ISP_HIST_V_SIZE);
+
+ weight = arg->hist_weight;
+ for (i = 0; i < ARRAY_SIZE(hist_weight_regs); ++i, weight += 4)
+ rkisp1_iowrite32(params_vdev, CIF_ISP_HIST_WEIGHT_SET(
+ weight[0], weight[1], weight[2], weight[3]),
+ hist_weight_regs[i]);
+}
+
+static void hst_enable(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_hst_config *arg, bool en)
+{
+ if (en) {
+ u32 hist_prop = rkisp1_ioread32(params_vdev, CIF_ISP_HIST_PROP);
+
+ hist_prop &= ~CIF_ISP_HIST_PROP_MODE_MASK;
+ hist_prop |= arg->mode;
+ isp_param_set_bits(params_vdev, CIF_ISP_HIST_PROP, hist_prop);
+ } else {
+ isp_param_clear_bits(params_vdev, CIF_ISP_HIST_PROP,
+ CIF_ISP_HIST_PROP_MODE_MASK);
+ }
+}
+
+static void afm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_afc_config *arg)
+{
+ int i;
+ size_t num_of_win = min_t(size_t, ARRAY_SIZE(arg->afm_win),
+ arg->num_afm_win);
+
+ /* Switch off to configure. Enabled during normal flow in frame isr. */
+ isp_param_clear_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+
+ for (i = 0; i < num_of_win; i++) {
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) |
+ CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs),
+ CIF_ISP_AFM_LT_A + i * 8);
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size +
+ arg->afm_win[i].h_offs) |
+ CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size +
+ arg->afm_win[i].v_offs),
+ CIF_ISP_AFM_RB_A + i * 8);
+ }
+ rkisp1_iowrite32(params_vdev, arg->thres, CIF_ISP_AFM_THRES);
+ rkisp1_iowrite32(params_vdev, arg->var_shift, CIF_ISP_AFM_VAR_SHIFT);
+}
+
+static void ie_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_ie_config *arg)
+{
+ u32 eff_ctrl;
+
+ eff_ctrl = rkisp1_ioread32(params_vdev, CIF_IMG_EFF_CTRL);
+ eff_ctrl &= ~CIF_IMG_EFF_CTRL_MODE_MASK;
+
+ if (params_vdev->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ eff_ctrl |= CIF_IMG_EFF_CTRL_YCBCR_FULL;
+
+ switch (arg->effect) {
+ case V4L2_COLORFX_SEPIA:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ case V4L2_COLORFX_SET_CBCR:
+ rkisp1_iowrite32(params_vdev, arg->eff_tint, CIF_IMG_EFF_TINT);
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ /*
+ * Color selection is similar to water color(AQUA):
+ * grayscale + selected color w threshold
+ */
+ case V4L2_COLORFX_AQUA:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_COLOR_SEL;
+ rkisp1_iowrite32(params_vdev, arg->color_sel,
+ CIF_IMG_EFF_COLOR_SEL);
+ break;
+ case V4L2_COLORFX_EMBOSS:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_EMBOSS;
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_1,
+ CIF_IMG_EFF_MAT_1);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_2,
+ CIF_IMG_EFF_MAT_2);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_3,
+ CIF_IMG_EFF_MAT_3);
+ break;
+ case V4L2_COLORFX_SKETCH:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SKETCH;
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_3,
+ CIF_IMG_EFF_MAT_3);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_4,
+ CIF_IMG_EFF_MAT_4);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_5,
+ CIF_IMG_EFF_MAT_5);
+ break;
+ case V4L2_COLORFX_BW:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_BLACKWHITE;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_NEGATIVE;
+ break;
+ default:
+ break;
+ }
+
+ rkisp1_iowrite32(params_vdev, eff_ctrl, CIF_IMG_EFF_CTRL);
+}
+
+static void ie_enable(struct rkisp1_isp_params_vdev *params_vdev, bool en)
+{
+ if (en) {
+ isp_param_set_bits(params_vdev, CIF_ICCL, CIF_ICCL_IE_CLK);
+ rkisp1_iowrite32(params_vdev, CIF_IMG_EFF_CTRL_ENABLE,
+ CIF_IMG_EFF_CTRL);
+ isp_param_set_bits(params_vdev, CIF_IMG_EFF_CTRL,
+ CIF_IMG_EFF_CTRL_CFG_UPD);
+ } else {
+ /* Disable measurement */
+ isp_param_clear_bits(params_vdev, CIF_IMG_EFF_CTRL,
+ CIF_IMG_EFF_CTRL_ENABLE);
+ isp_param_clear_bits(params_vdev, CIF_ICCL, CIF_ICCL_IE_CLK);
+ }
+}
+
+static void csm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ bool full_range)
+{
+ static const u16 full_range_coeff[] = {
+ 0x0026, 0x004b, 0x000f,
+ 0x01ea, 0x01d6, 0x0040,
+ 0x0040, 0x01ca, 0x01f6
+ };
+ static const u16 limited_range_coeff[] = {
+ 0x0021, 0x0040, 0x000d,
+ 0x01ed, 0x01db, 0x0038,
+ 0x0038, 0x01d1, 0x01f7,
+ };
+ int i;
+
+ if (full_range) {
+ for (i = 0; i < ARRAY_SIZE(full_range_coeff); i++)
+ rkisp1_iowrite32(params_vdev, full_range_coeff[i],
+ CIF_ISP_CC_COEFF_0 + i * 4);
+
+ isp_param_set_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(limited_range_coeff); i++)
+ rkisp1_iowrite32(params_vdev, limited_range_coeff[i],
+ CIF_ISP_CC_COEFF_0 + i * 4);
+
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ }
+}
+
+/* ISP De-noise Pre-Filter(DPF) function */
+static void dpf_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpf_config *arg)
+{
+ unsigned int isp_dpf_mode;
+ unsigned int spatial_coeff;
+ unsigned int i;
+
+ switch (arg->gain.mode) {
+ case CIFISP_DPF_GAIN_USAGE_NF_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP |
+ CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_AWB_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_LSC_GAIN_COMP |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_DISABLED:
+ default:
+ isp_dpf_mode = 0;
+ break;
+ }
+
+ if (arg->nll.scale_mode == CIFISP_NLL_SCALE_LOGARITHMIC)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_NLL_SEGMENTATION;
+ if (arg->rb_flt.fltsize == CIFISP_DPF_RB_FILTERSIZE_9x9)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_RB_FLTSIZE_9x9;
+ if (!arg->rb_flt.r_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_R_FLT_DIS;
+ if (!arg->rb_flt.b_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_B_FLT_DIS;
+ if (!arg->g_flt.gb_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_GB_FLT_DIS;
+ if (!arg->g_flt.gr_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_GR_FLT_DIS;
+
+ isp_param_set_bits(params_vdev, CIF_ISP_DPF_MODE, isp_dpf_mode);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_b_gain,
+ CIF_ISP_DPF_NF_GAIN_B);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_r_gain,
+ CIF_ISP_DPF_NF_GAIN_R);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_gb_gain,
+ CIF_ISP_DPF_NF_GAIN_GB);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_gr_gain,
+ CIF_ISP_DPF_NF_GAIN_GR);
+
+ for (i = 0; i < CIFISP_DPF_MAX_NLF_COEFFS; i++) {
+ rkisp1_iowrite32(params_vdev, arg->nll.coeff[i],
+ CIF_ISP_DPF_NULL_COEFF_0 + i * 4);
+ }
+
+ spatial_coeff = arg->g_flt.spatial_coeff[0] |
+ (arg->g_flt.spatial_coeff[1] << 8) |
+ (arg->g_flt.spatial_coeff[2] << 16) |
+ (arg->g_flt.spatial_coeff[3] << 24);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_G_1_4);
+
+ spatial_coeff = arg->g_flt.spatial_coeff[4] |
+ (arg->g_flt.spatial_coeff[5] << 8);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_G_5_6);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[0] |
+ (arg->rb_flt.spatial_coeff[1] << 8) |
+ (arg->rb_flt.spatial_coeff[2] << 16) |
+ (arg->rb_flt.spatial_coeff[3] << 24);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_RB_1_4);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[4] |
+ (arg->rb_flt.spatial_coeff[5] << 8);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_RB_5_6);
+}
+
+static void dpf_strength_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpf_strength_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->b, CIF_ISP_DPF_STRENGTH_B);
+ rkisp1_iowrite32(params_vdev, arg->g, CIF_ISP_DPF_STRENGTH_G);
+ rkisp1_iowrite32(params_vdev, arg->r, CIF_ISP_DPF_STRENGTH_R);
+}
+
+static __maybe_unused
+void __isp_isr_other_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct rkisp1_isp_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ if ((module_en_update & CIFISP_MODULE_DPCC) ||
+ (module_cfg_update & CIFISP_MODULE_DPCC)) {
+ /*update dpc config */
+ if ((module_cfg_update & CIFISP_MODULE_DPCC))
+ dpcc_config(params_vdev,
+ &new_params->others.dpcc_config);
+
+ if (module_en_update & CIFISP_MODULE_DPCC) {
+ if (!!(module_ens & CIFISP_MODULE_DPCC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DPCC_MODE,
+ CIF_ISP_DPCC_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DPCC_MODE,
+ CIF_ISP_DPCC_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_BLS) ||
+ (module_cfg_update & CIFISP_MODULE_BLS)) {
+ /* update bls config */
+ if ((module_cfg_update & CIFISP_MODULE_BLS))
+ bls_config(params_vdev, &new_params->others.bls_config);
+
+ if (module_en_update & CIFISP_MODULE_BLS) {
+ if (!!(module_ens & CIFISP_MODULE_BLS))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_BLS_CTRL,
+ CIF_ISP_BLS_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_BLS_CTRL,
+ CIF_ISP_BLS_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_SDG) ||
+ (module_cfg_update & CIFISP_MODULE_SDG)) {
+ /* update sdg config */
+ if ((module_cfg_update & CIFISP_MODULE_SDG))
+ sdg_config(params_vdev, &new_params->others.sdg_config);
+
+ if (module_en_update & CIFISP_MODULE_SDG) {
+ if (!!(module_ens & CIFISP_MODULE_SDG))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_LSC) ||
+ (module_cfg_update & CIFISP_MODULE_LSC)) {
+ /* update lsc config */
+ if ((module_cfg_update & CIFISP_MODULE_LSC))
+ lsc_config(params_vdev, &new_params->others.lsc_config);
+
+ if (module_en_update & CIFISP_MODULE_LSC) {
+ if (!!(module_ens & CIFISP_MODULE_LSC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AWB_GAIN) ||
+ (module_cfg_update & CIFISP_MODULE_AWB_GAIN)) {
+ /* update awb gains */
+ if ((module_cfg_update & CIFISP_MODULE_AWB_GAIN))
+ awb_gain_config(params_vdev,
+ &new_params->others.awb_gain_config);
+
+ if (module_en_update & CIFISP_MODULE_AWB_GAIN) {
+ if (!!(module_ens & CIFISP_MODULE_AWB_GAIN))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_BDM) ||
+ (module_cfg_update & CIFISP_MODULE_BDM)) {
+ /* update bdm config */
+ if ((module_cfg_update & CIFISP_MODULE_BDM))
+ bdm_config(params_vdev, &new_params->others.bdm_config);
+
+ if (module_en_update & CIFISP_MODULE_BDM) {
+ if (!!(module_ens & CIFISP_MODULE_BDM))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_FLT) ||
+ (module_cfg_update & CIFISP_MODULE_FLT)) {
+ /* update filter config */
+ if ((module_cfg_update & CIFISP_MODULE_FLT))
+ flt_config(params_vdev, &new_params->others.flt_config);
+
+ if (module_en_update & CIFISP_MODULE_FLT) {
+ if (!!(module_ens & CIFISP_MODULE_FLT))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_FILT_MODE,
+ CIF_ISP_FLT_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_FILT_MODE,
+ CIF_ISP_FLT_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_CTK) ||
+ (module_cfg_update & CIFISP_MODULE_CTK)) {
+ /* update ctk config */
+ if ((module_cfg_update & CIFISP_MODULE_CTK))
+ ctk_config(params_vdev, &new_params->others.ctk_config);
+
+ if (module_en_update & CIFISP_MODULE_CTK)
+ ctk_enable(params_vdev,
+ !!(module_ens & CIFISP_MODULE_CTK));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_GOC) ||
+ (module_cfg_update & CIFISP_MODULE_GOC)) {
+ /* update goc config */
+ if ((module_cfg_update & CIFISP_MODULE_GOC))
+ goc_config(params_vdev, &new_params->others.goc_config);
+
+ if (module_en_update & CIFISP_MODULE_GOC) {
+ if (!!(module_ens & CIFISP_MODULE_GOC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_CPROC) ||
+ (module_cfg_update & CIFISP_MODULE_CPROC)) {
+ /* update cproc config */
+ if ((module_cfg_update & CIFISP_MODULE_CPROC)) {
+ cproc_config(params_vdev,
+ &new_params->others.cproc_config);
+
+ }
+
+ if (module_en_update & CIFISP_MODULE_CPROC) {
+ if (!!(module_ens & CIFISP_MODULE_CPROC))
+ isp_param_set_bits(params_vdev,
+ CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ }
+ }
+
+
+ if ((module_en_update & CIFISP_MODULE_IE) ||
+ (module_cfg_update & CIFISP_MODULE_IE)) {
+ /* update ie config */
+ if ((module_cfg_update & CIFISP_MODULE_IE))
+ ie_config(params_vdev, &new_params->others.ie_config);
+
+ if (module_en_update & CIFISP_MODULE_IE)
+ ie_enable(params_vdev,
+ !!(module_ens & CIFISP_MODULE_IE));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_DPF) ||
+ (module_cfg_update & CIFISP_MODULE_DPF)) {
+ /* update dpf config */
+ if ((module_cfg_update & CIFISP_MODULE_DPF))
+ dpf_config(params_vdev, &new_params->others.dpf_config);
+
+ if (module_en_update & CIFISP_MODULE_DPF) {
+ if (!!(module_ens & CIFISP_MODULE_DPF))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_DPF_STRENGTH) ||
+ (module_cfg_update & CIFISP_MODULE_DPF_STRENGTH)) {
+ /* update dpf strength config */
+ dpf_strength_config(params_vdev,
+ &new_params->others.dpf_strength_config);
+ }
+}
+
+static __maybe_unused
+void __isp_isr_meas_config(struct rkisp1_isp_params_vdev *params_vdev,
+ struct rkisp1_isp_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ if ((module_en_update & CIFISP_MODULE_AWB) ||
+ (module_cfg_update & CIFISP_MODULE_AWB)) {
+ /* update awb config */
+ if ((module_cfg_update & CIFISP_MODULE_AWB))
+ awb_meas_config(params_vdev,
+ &new_params->meas.awb_meas_config);
+
+ if (module_en_update & CIFISP_MODULE_AWB)
+ awb_meas_enable(params_vdev,
+ &new_params->meas.awb_meas_config,
+ !!(module_ens & CIFISP_MODULE_AWB));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AFC) ||
+ (module_cfg_update & CIFISP_MODULE_AFC)) {
+ /* update afc config */
+ if ((module_cfg_update & CIFISP_MODULE_AFC))
+ afm_config(params_vdev, &new_params->meas.afc_config);
+
+ if (module_en_update & CIFISP_MODULE_AFC) {
+ if (!!(module_ens & CIFISP_MODULE_AFC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_AFM_CTRL,
+ CIF_ISP_AFM_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_AFM_CTRL,
+ CIF_ISP_AFM_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_HST) ||
+ (module_cfg_update & CIFISP_MODULE_HST)) {
+ /* update hst config */
+ if ((module_cfg_update & CIFISP_MODULE_HST))
+ hst_config(params_vdev, &new_params->meas.hst_config);
+
+ if (module_en_update & CIFISP_MODULE_HST)
+ hst_enable(params_vdev,
+ &new_params->meas.hst_config,
+ !!(module_ens & CIFISP_MODULE_HST));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AEC) ||
+ (module_cfg_update & CIFISP_MODULE_AEC)) {
+ /* update aec config */
+ if ((module_cfg_update & CIFISP_MODULE_AEC))
+ aec_config(params_vdev, &new_params->meas.aec_config);
+
+ if (module_en_update & CIFISP_MODULE_AEC) {
+ if (!!(module_ens & CIFISP_MODULE_AEC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_EXP_CTRL,
+ CIF_ISP_EXP_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_EXP_CTRL,
+ CIF_ISP_EXP_ENA);
+ }
+ }
+}
+
+void rkisp1_params_isr(struct rkisp1_isp_params_vdev *params_vdev, u32 isp_mis)
+{
+ struct rkisp1_isp_params_cfg *new_params;
+ struct rkisp1_buffer *cur_buf = NULL;
+ unsigned int cur_frame_id = -1;
+ cur_frame_id = atomic_read(¶ms_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+
+ spin_lock(¶ms_vdev->config_lock);
+ if (!params_vdev->streamon) {
+ spin_unlock(¶ms_vdev->config_lock);
+ return;
+ }
+
+ /* get one empty buffer */
+ if (!list_empty(¶ms_vdev->params))
+ cur_buf = list_first_entry(¶ms_vdev->params,
+ struct rkisp1_buffer, queue);
+ spin_unlock(¶ms_vdev->config_lock);
+
+ if (!cur_buf)
+ return;
+
+ new_params = (struct rkisp1_isp_params_cfg *)(cur_buf->vaddr[0]);
+
+ if (isp_mis & CIF_ISP_FRAME) {
+ __isp_isr_other_config(params_vdev, new_params);
+ __isp_isr_meas_config(params_vdev, new_params);
+ spin_lock(¶ms_vdev->config_lock);
+ list_del(&cur_buf->queue);
+ spin_unlock(¶ms_vdev->config_lock);
+
+ cur_buf->vb.sequence = cur_frame_id;
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+}
+
+static const struct cifisp_awb_meas_config awb_params_default_config = {
+ {
+ 0, 0, RKISP1_DEFAULT_WIDTH, RKISP1_DEFAULT_HEIGHT
+ },
+ CIFISP_AWB_MODE_YCBCR, 200, 30, 20, 20, 0, 128, 128
+};
+
+static const struct cifisp_aec_config aec_params_default_config = {
+ CIFISP_EXP_MEASURING_MODE_0,
+ CIFISP_EXP_CTRL_AUTOSTOP_0,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ }
+};
+
+static const struct cifisp_hst_config hst_params_default_config = {
+ CIFISP_HISTOGRAM_MODE_RGB_COMBINED,
+ 3,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ },
+ {
+ 0, /* To be filled in with 0x01 at runtime. */
+ }
+};
+
+static const struct cifisp_afc_config afc_params_default_config = {
+ 1,
+ {
+ {
+ 300, 225, 200, 150
+ }
+ },
+ 4,
+ 14
+};
+
+static
+void rkisp1_params_config_parameter(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct cifisp_hst_config hst = hst_params_default_config;
+
+ spin_lock(¶ms_vdev->config_lock);
+
+ awb_meas_config(params_vdev, &awb_params_default_config);
+ awb_meas_enable(params_vdev, &awb_params_default_config, true);
+
+ aec_config(params_vdev, &aec_params_default_config);
+ isp_param_set_bits(params_vdev, CIF_ISP_EXP_CTRL, CIF_ISP_EXP_ENA);
+
+ afm_config(params_vdev, &afc_params_default_config);
+ isp_param_set_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+
+ memset(hst.hist_weight, 0x01, sizeof(hst.hist_weight));
+ hst_config(params_vdev, &hst);
+ isp_param_set_bits(params_vdev, CIF_ISP_HIST_PROP,
+ ~CIF_ISP_HIST_PROP_MODE_MASK |
+ hst_params_default_config.mode);
+
+ /* set the range */
+ if (params_vdev->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ csm_config(params_vdev, true);
+ else
+ csm_config(params_vdev, false);
+
+ /* override the default things */
+ __isp_isr_other_config(params_vdev, ¶ms_vdev->cur_params);
+ __isp_isr_meas_config(params_vdev, ¶ms_vdev->cur_params);
+
+ spin_unlock(¶ms_vdev->config_lock);
+}
+
+/* Not called when the camera active, thus not isr protection. */
+void rkisp1_params_configure_isp(struct rkisp1_isp_params_vdev *params_vdev,
+ struct ispsd_in_fmt *in_fmt,
+ enum v4l2_quantization quantization)
+{
+ params_vdev->quantization = quantization;
+ params_vdev->raw_type = in_fmt->bayer_pat;
+ rkisp1_params_config_parameter(params_vdev);
+}
+
+/* Not called when the camera active, thus not isr protection. */
+void rkisp1_params_disable_isp(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ isp_param_clear_bits(params_vdev, CIF_ISP_DPCC_MODE, CIF_ISP_DPCC_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_BLS_CTRL, CIF_ISP_BLS_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ isp_param_clear_bits(params_vdev, CIF_ISP_FILT_MODE, CIF_ISP_FLT_ENA);
+ awb_meas_enable(params_vdev, NULL, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_EXP_CTRL, CIF_ISP_EXP_ENA);
+ ctk_enable(params_vdev, false);
+ isp_param_clear_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ hst_enable(params_vdev, NULL, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+ ie_enable(params_vdev, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+}
+
+static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_params_vdev *params_vdev = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = params_vdev->vdev_fmt.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_params_vdev *params_vdev = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = params_vdev->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = params_vdev->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_params_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strcpy(cap->driver, DRIVER_NAME);
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP params video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_params_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_querycap = rkisp1_params_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+
+ *num_buffers = clamp_t(u32, *num_buffers,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MIN,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MAX);
+
+ *num_planes = 1;
+
+ sizes[0] = sizeof(struct rkisp1_isp_params_cfg);
+
+ INIT_LIST_HEAD(¶ms_vdev->params);
+ params_vdev->first_params = true;
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *params_buf = to_rkisp1_buffer(vbuf);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+ struct rkisp1_isp_params_cfg *new_params;
+ unsigned long flags;
+
+ unsigned int cur_frame_id = -1;
+ cur_frame_id = atomic_read(¶ms_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+
+ if (params_vdev->first_params) {
+ new_params = (struct rkisp1_isp_params_cfg *)
+ (vb2_plane_vaddr(vb, 0));
+ vbuf->sequence = cur_frame_id;
+ vb2_buffer_done(¶ms_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ params_vdev->first_params = false;
+ params_vdev->cur_params = *new_params;
+ return;
+ }
+
+ params_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ list_add_tail(¶ms_buf->queue, ¶ms_vdev->params);
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+}
+
+static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_isp_params_cfg))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_isp_params_cfg));
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ /* stop params input firstly */
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ params_vdev->streamon = false;
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+
+ for (i = 0; i < RKISP1_ISP_PARAMS_REQ_BUFS_MAX; i++) {
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ if (!list_empty(¶ms_vdev->params)) {
+ buf = list_first_entry(¶ms_vdev->params,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(¶ms_vdev->config_lock,
+ flags);
+ } else {
+ spin_unlock_irqrestore(¶ms_vdev->config_lock,
+ flags);
+ break;
+ }
+
+ if (buf)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ buf = NULL;
+ }
+}
+
+static int
+rkisp1_params_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct rkisp1_isp_params_vdev *params_vdev = queue->drv_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ params_vdev->streamon = true;
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops rkisp1_params_vb2_ops = {
+ .queue_setup = rkisp1_params_vb2_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = rkisp1_params_vb2_buf_queue,
+ .buf_prepare = rkisp1_params_vb2_buf_prepare,
+ .start_streaming = rkisp1_params_vb2_start_streaming,
+ .stop_streaming = rkisp1_params_vb2_stop_streaming,
+
+};
+
+struct v4l2_file_operations rkisp1_params_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int
+rkisp1_params_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = queue_to_node(q);
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = params_vdev;
+ q->ops = &rkisp1_params_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_init_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ params_vdev->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_PARAMS;
+ params_vdev->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_isp_params_cfg);
+}
+
+int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev)
+{
+ int ret;
+ struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ params_vdev->dev = dev;
+ mutex_init(&node->vlock);
+ spin_lock_init(¶ms_vdev->config_lock);
+
+ strlcpy(vdev->name, "rkisp1-input-params", sizeof(vdev->name));
+
+ video_set_drvdata(vdev, params_vdev);
+ vdev->ioctl_ops = &rkisp1_params_ioctl;
+ vdev->fops = &rkisp1_params_fops;
+ vdev->release = video_device_release_empty;
+ /*
+ * Provide a mutex to v4l2 core. It will be used
+ * to protect all fops and v4l2 ioctls.
+ */
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+ vdev->vfl_dir = VFL_DIR_TX;
+ rkisp1_params_init_vb2_queue(vdev->queue, params_vdev);
+ rkisp1_init_params_vdev(params_vdev);
+ video_set_drvdata(vdev, params_vdev);
+
+ node->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret < 0)
+ goto err_release_queue;
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(&vdev->dev,
+ "could not register Video for Linux device\n");
+ goto err_cleanup_media_entity;
+ }
+ return 0;
+err_cleanup_media_entity:
+ media_entity_cleanup(&vdev->entity);
+err_release_queue:
+ vb2_queue_release(vdev->queue);
+ return ret;
+}
+
+void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ vb2_queue_release(vdev->queue);
+}
diff --git a/drivers/media/platform/rockchip/isp1/isp_params.h b/drivers/media/platform/rockchip/isp1/isp_params.h
new file mode 100644
index 000000000000..24602e11014d
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_params.h
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_ISP_H
+#define _RKISP1_ISP_H
+
+#include <linux/rkisp1-config.h>
+#include "common.h"
+
+/*
+ * struct rkisp1_isp_subdev - ISP input parameters device
+ *
+ * @cur_params: Current ISP parameters
+ * @first_params: the first params should take effect immediately
+ */
+struct rkisp1_isp_params_vdev {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *dev;
+
+ spinlock_t config_lock;
+ struct list_head params;
+ struct rkisp1_isp_params_cfg cur_params;
+ struct v4l2_format vdev_fmt;
+ bool streamon;
+ bool first_params;
+
+ enum v4l2_quantization quantization;
+ enum rkisp1_fmt_raw_pat_type raw_type;
+};
+
+/* config params before ISP streaming */
+void rkisp1_params_configure_isp(struct rkisp1_isp_params_vdev *params_vdev,
+ struct ispsd_in_fmt *in_fmt,
+ enum v4l2_quantization quantization);
+void rkisp1_params_disable_isp(struct rkisp1_isp_params_vdev *params_vdev);
+
+int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev);
+
+void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev);
+
+void rkisp1_params_isr(struct rkisp1_isp_params_vdev *params_vdev, u32 isp_mis);
+
+#endif /* _RKISP1_ISP_H */
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (6 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 07/17] media: rkisp1: add ISP1 params driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-11 6:58 ` Baruch Siach
2018-05-16 8:08 ` Tomasz Figa
2018-03-08 9:48 ` [PATCH v6 10/17] dt-bindings: Document the Rockchip ISP1 bindings Jacob Chen
` (12 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the core driver for rockchip isp1.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/Kconfig | 10 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/rockchip/isp1/Makefile | 8 +
drivers/media/platform/rockchip/isp1/common.h | 110 +++++
drivers/media/platform/rockchip/isp1/dev.c | 626 ++++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/dev.h | 93 ++++
6 files changed, 848 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
create mode 100644 drivers/media/platform/rockchip/isp1/common.h
create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 614fbef08ddc..3dff763b731d 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -117,6 +117,16 @@ config VIDEO_QCOM_CAMSS
select VIDEOBUF2_DMA_SG
select V4L2_FWNODE
+config VIDEO_ROCKCHIP_ISP1
+ tristate "Rockchip Image Signal Processing v1 Unit driver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ default n
+ ---help---
+ Support for ISP1 on the rockchip SoC.
+
config VIDEO_S3C_CAMIF
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 7f3080437be6..0d5e1b3e6f22 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
+obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip/isp1/
obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/
obj-y += omap/
diff --git a/drivers/media/platform/rockchip/isp1/Makefile b/drivers/media/platform/rockchip/isp1/Makefile
new file mode 100644
index 000000000000..18af64853734
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += video_rkisp1.o
+video_rkisp1-objs += rkisp1.o \
+ dev.o \
+ regs.o \
+ isp_stats.o \
+ isp_params.o \
+ mipi_dphy_sy.o \
+ capture.o
diff --git a/drivers/media/platform/rockchip/isp1/common.h b/drivers/media/platform/rockchip/isp1/common.h
new file mode 100644
index 000000000000..b58cdaf7da06
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/common.h
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_COMMON_H
+#define _RKISP1_COMMON_H
+
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+#define RKISP1_DEFAULT_WIDTH 800
+#define RKISP1_DEFAULT_HEIGHT 600
+
+#define RKISP1_MAX_STREAM 2
+#define RKISP1_STREAM_SP 0
+#define RKISP1_STREAM_MP 1
+
+#define RKISP1_PLANE_Y 0
+#define RKISP1_PLANE_CB 1
+#define RKISP1_PLANE_CR 2
+
+enum rkisp1_sd_type {
+ RKISP1_SD_SENSOR,
+ RKISP1_SD_PHY_CSI,
+ RKISP1_SD_VCM,
+ RKISP1_SD_FLASH,
+ RKISP1_SD_MAX,
+};
+
+/* One structure per video node */
+struct rkisp1_vdev_node {
+ struct vb2_queue buf_queue;
+ /* vfd lock */
+ struct mutex vlock;
+ struct video_device vdev;
+ struct media_pad pad;
+};
+
+enum rkisp1_fmt_pix_type {
+ FMT_YUV,
+ FMT_RGB,
+ FMT_BAYER,
+ FMT_JPEG,
+ FMT_MAX
+};
+
+enum rkisp1_fmt_raw_pat_type {
+ RAW_RGGB = 0,
+ RAW_GRBG,
+ RAW_GBRG,
+ RAW_BGGR,
+};
+
+enum rkisp1_state {
+ /* path not yet opened: */
+ RKISP1_STATE_DISABLED,
+ /* path opened and configured, ready for streaming: */
+ RKISP1_STATE_READY,
+ /* path is streaming: */
+ RKISP1_STATE_STREAMING
+};
+
+struct rkisp1_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ union {
+ u32 buff_addr[VIDEO_MAX_PLANES];
+ void *vaddr[VIDEO_MAX_PLANES];
+ };
+};
+
+struct rkisp1_dummy_buffer {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+extern int rkisp1_debug;
+
+static inline
+struct rkisp1_vdev_node *vdev_to_node(struct video_device *vdev)
+{
+ return container_of(vdev, struct rkisp1_vdev_node, vdev);
+}
+
+static inline struct rkisp1_vdev_node *queue_to_node(struct vb2_queue *q)
+{
+ return container_of(q, struct rkisp1_vdev_node, buf_queue);
+}
+
+static inline struct rkisp1_buffer *to_rkisp1_buffer(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct rkisp1_buffer, vb);
+}
+
+static inline struct vb2_queue *to_vb2_queue(struct file *file)
+{
+ struct rkisp1_vdev_node *vnode = video_drvdata(file);
+
+ return &vnode->buf_queue;
+}
+
+#endif /* _RKISP1_COMMON_H */
diff --git a/drivers/media/platform/rockchip/isp1/dev.c b/drivers/media/platform/rockchip/isp1/dev.c
new file mode 100644
index 000000000000..990ac28e4107
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/dev.c
@@ -0,0 +1,626 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include "common.h"
+#include "regs.h"
+
+struct isp_match_data {
+ const char * const *clks;
+ int size;
+};
+
+int rkisp1_debug;
+module_param_named(debug, rkisp1_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/***************************** pipeline operations*******************************/
+
+static int __isp_pipeline_prepare(struct rkisp1_pipeline *p,
+ struct media_entity *me)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ struct v4l2_subdev *sd;
+ int i;
+
+ p->num_subdevs = 0;
+ memset(p->subdevs, 0, sizeof(p->subdevs));
+
+ while (1) {
+ struct media_pad *pad = NULL;
+
+ /* Find remote source pad */
+ for (i = 0; i < me->num_pads; i++) {
+ struct media_pad *spad = &me->pads[i];
+
+ if (!(spad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+ pad = media_entity_remote_pad(spad);
+ if (pad)
+ break;
+ }
+
+ if (!pad)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ if (sd != &dev->isp_sdev.sd)
+ p->subdevs[p->num_subdevs++] = sd;
+
+ me = &sd->entity;
+ if (me->num_pads == 1)
+ break;
+ }
+ return 0;
+}
+
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int ret;
+
+ if (!sd)
+ return -ENXIO;
+
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+static int __isp_pipeline_s_power(struct rkisp1_pipeline *p, bool on)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ int i, ret;
+
+ if (on) {
+ __subdev_set_power(&dev->isp_sdev.sd, true);
+
+ for (i = p->num_subdevs - 1; i >= 0; --i) {
+ ret = __subdev_set_power(p->subdevs[i], true);
+ if (ret < 0 && ret != -ENXIO)
+ goto err_power_off;
+ }
+ } else {
+ for (i = 0; i < p->num_subdevs; ++i)
+ __subdev_set_power(p->subdevs[i], false);
+
+ __subdev_set_power(&dev->isp_sdev.sd, false);
+ }
+
+ return 0;
+
+err_power_off:
+ for (++i; i < p->num_subdevs; ++i)
+ __subdev_set_power(p->subdevs[i], false);
+ __subdev_set_power(&dev->isp_sdev.sd, true);
+ return ret;
+}
+
+static int rkisp1_pipeline_open(struct rkisp1_pipeline *p,
+ struct media_entity *me,
+ bool prepare)
+{
+ int ret;
+
+ if (WARN_ON(!p || !me))
+ return -EINVAL;
+ if (atomic_inc_return(&p->power_cnt) > 1)
+ return 0;
+
+ /* go through media graphic and get subdevs */
+ if (prepare)
+ __isp_pipeline_prepare(p, me);
+
+ if (!p->num_subdevs)
+ return -EINVAL;
+
+ ret = __isp_pipeline_s_power(p, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rkisp1_pipeline_close(struct rkisp1_pipeline *p)
+{
+ int ret;
+
+ if (atomic_dec_return(&p->power_cnt) > 0)
+ return 0;
+ ret = __isp_pipeline_s_power(p, 0);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/*
+ * stream-on order: isp_subdev, mipi dphy, sensor
+ * stream-off order: mipi dphy, sensor, isp_subdev
+ */
+static int rkisp1_pipeline_set_stream(struct rkisp1_pipeline *p, bool on)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ int i, ret;
+
+ if ((on && atomic_inc_return(&p->stream_cnt) > 1) ||
+ (!on && atomic_dec_return(&p->stream_cnt) > 0))
+ return 0;
+
+ if (on)
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, true);
+
+ /* phy -> sensor */
+ for (i = 0; i < p->num_subdevs; ++i) {
+ ret = v4l2_subdev_call(p->subdevs[i], video, s_stream, on);
+ if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto err_stream_off;
+ }
+
+ if (!on)
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, false);
+
+ return 0;
+
+err_stream_off:
+ for (--i; i >= 0; --i)
+ v4l2_subdev_call(p->subdevs[i], video, s_stream, false);
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, false);
+ return ret;
+}
+
+/***************************** media controller *******************************/
+/* See http://opensource.rock-chips.com/wiki_Rockchip-isp1 for Topology */
+
+static int rkisp1_create_links(struct rkisp1_device *dev)
+{
+ struct media_entity *source, *sink;
+ unsigned int flags, s, pad;
+ int ret;
+
+ /* sensor links(or mipi-phy) */
+ for (s = 0; s < dev->num_sensors; ++s) {
+ struct rkisp1_sensor_info *sensor = &dev->sensors[s];
+
+ for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
+ if (sensor->sd->entity.pads[pad].flags &
+ MEDIA_PAD_FL_SOURCE)
+ break;
+
+ if (pad == sensor->sd->entity.num_pads) {
+ dev_err(dev->dev,
+ "failed to find src pad for %s\n",
+ sensor->sd->name);
+
+ return -ENXIO;
+ }
+
+ ret = media_create_pad_link(
+ &sensor->sd->entity, pad,
+ &dev->isp_sdev.sd.entity,
+ RKISP1_ISP_PAD_SINK + s,
+ s ? 0 : MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(dev->dev,
+ "failed to create link for %s\n",
+ sensor->sd->name);
+ return ret;
+ }
+ }
+
+ /* params links */
+ source = &dev->params_vdev.vnode.vdev.entity;
+ sink = &dev->isp_sdev.sd.entity;
+ flags = MEDIA_LNK_FL_ENABLED;
+ ret = media_create_pad_link(source, 0, sink,
+ RKISP1_ISP_PAD_SINK_PARAMS, flags);
+ if (ret < 0)
+ return ret;
+
+ /* create isp internal links */
+ /* SP links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stream[RKISP1_STREAM_SP].vnode.vdev.entity;
+ ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_PATH,
+ sink, 0, flags);
+ if (ret < 0)
+ return ret;
+
+ /* MP links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stream[RKISP1_STREAM_MP].vnode.vdev.entity;
+ ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_PATH,
+ sink, 0, flags);
+ if (ret < 0)
+ return ret;
+
+ /* 3A stats links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stats_vdev.vnode.vdev.entity;
+ return media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_STATS,
+ sink, 0, flags);
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rkisp1_device *dev;
+ int ret;
+
+ dev = container_of(notifier, struct rkisp1_device, notifier);
+
+ mutex_lock(&dev->media_dev.graph_mutex);
+ ret = rkisp1_create_links(dev);
+ if (ret < 0)
+ goto unlock;
+ ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
+ if (ret < 0)
+ goto unlock;
+
+ v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
+
+unlock:
+ mutex_unlock(&dev->media_dev.graph_mutex);
+ return ret;
+}
+
+struct rkisp1_async_subdev {
+ struct v4l2_async_subdev asd;
+ struct v4l2_mbus_config mbus;
+};
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rkisp1_device *isp_dev = container_of(notifier,
+ struct rkisp1_device, notifier);
+ struct rkisp1_async_subdev *s_asd = container_of(asd,
+ struct rkisp1_async_subdev, asd);
+
+ if (isp_dev->num_sensors == ARRAY_SIZE(isp_dev->sensors))
+ return -EBUSY;
+
+ isp_dev->sensors[isp_dev->num_sensors].mbus = s_asd->mbus;
+ isp_dev->sensors[isp_dev->num_sensors].sd = subdev;
+ ++isp_dev->num_sensors;
+
+ v4l2_dbg(1, rkisp1_debug, subdev, "Async registered subdev\n");
+
+ return 0;
+}
+
+static int rkisp1_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct rkisp1_async_subdev *rk_asd =
+ container_of(asd, struct rkisp1_async_subdev, asd);
+ struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
+
+ /*
+ * MIPI sensor is linked with a mipi dphy and its media bus config can
+ * not be get in here
+ */
+ if (vep->bus_type != V4L2_MBUS_BT656 &&
+ vep->bus_type != V4L2_MBUS_PARALLEL)
+ return 0;
+
+ rk_asd->mbus.flags = bus->flags;
+ rk_asd->mbus.type = vep->bus_type;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
+ .bound = subdev_notifier_bound,
+ .complete = subdev_notifier_complete,
+};
+
+static int isp_subdev_notifier(struct rkisp1_device *isp_dev)
+{
+ struct v4l2_async_notifier *ntf = &isp_dev->notifier;
+ struct device *dev = isp_dev->dev;
+ int ret;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(
+ dev, ntf, sizeof(struct rkisp1_async_subdev),
+ rkisp1_fwnode_parse);
+ if (ret < 0)
+ return ret;
+
+ if (!ntf->num_subdevs)
+ return -ENODEV; /* no endpoint */
+
+ ntf->ops = &subdev_notifier_ops;
+
+ return v4l2_async_notifier_register(&isp_dev->v4l2_dev, ntf);
+}
+
+/***************************** platform deive *******************************/
+
+static int rkisp1_register_platform_subdevs(struct rkisp1_device *dev)
+{
+ int ret;
+
+ ret = rkisp1_register_isp_subdev(dev, &dev->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = rkisp1_register_stream_vdevs(dev);
+ if (ret < 0)
+ goto err_unreg_isp_subdev;
+
+ ret = rkisp1_register_stats_vdev(&dev->stats_vdev, &dev->v4l2_dev, dev);
+ if (ret < 0)
+ goto err_unreg_stream_vdev;
+
+ ret = rkisp1_register_params_vdev(&dev->params_vdev, &dev->v4l2_dev,
+ dev);
+ if (ret < 0)
+ goto err_unreg_stats_vdev;
+
+ ret = isp_subdev_notifier(dev);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to register subdev notifier(%d)\n", ret);
+ goto err_unreg_params_vdev;
+ }
+
+ return 0;
+err_unreg_params_vdev:
+ rkisp1_unregister_params_vdev(&dev->params_vdev);
+err_unreg_stats_vdev:
+ rkisp1_unregister_stats_vdev(&dev->stats_vdev);
+err_unreg_stream_vdev:
+ rkisp1_unregister_stream_vdevs(dev);
+err_unreg_isp_subdev:
+ rkisp1_unregister_isp_subdev(dev);
+ return ret;
+}
+
+static const char * const rk3399_isp_clks[] = {
+ "clk_isp",
+ "aclk_isp",
+ "hclk_isp",
+ "aclk_isp_wrap",
+ "hclk_isp_wrap",
+};
+
+static const char * const rk3288_isp_clks[] = {
+ "clk_isp",
+ "aclk_isp",
+ "hclk_isp",
+ "pclk_isp_in",
+ "sclk_isp_jpe",
+};
+
+static const struct isp_match_data rk3288_isp_clk_data = {
+ .clks = rk3288_isp_clks,
+ .size = ARRAY_SIZE(rk3288_isp_clks),
+};
+
+static const struct isp_match_data rk3399_isp_clk_data = {
+ .clks = rk3399_isp_clks,
+ .size = ARRAY_SIZE(rk3399_isp_clks),
+};
+
+static const struct of_device_id rkisp1_plat_of_match[] = {
+ {
+ .compatible = "rockchip,rk3288-cif-isp",
+ .data = &rk3288_isp_clk_data,
+ }, {
+ .compatible = "rockchip,rk3399-cif-isp",
+ .data = &rk3399_isp_clk_data,
+ },
+ {},
+};
+
+static irqreturn_t rkisp1_irq_handler(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkisp1_device *rkisp1_dev = dev_get_drvdata(dev);
+ void __iomem *base = rkisp1_dev->base_addr;
+ unsigned int mis_val, i;
+
+ mis_val = readl(rkisp1_dev->base_addr + CIF_ISP_MIS);
+ if (mis_val)
+ rkisp1_isp_isr(mis_val, rkisp1_dev);
+
+ mis_val = readl(rkisp1_dev->base_addr + CIF_MIPI_MIS);
+ if (mis_val)
+ rkisp1_mipi_isr(mis_val, rkisp1_dev);
+
+ for (i = 0; i < RKISP1_MAX_STREAM; ++i) {
+ struct rkisp1_stream *stream = &rkisp1_dev->stream[i];
+
+ if (stream->ops->is_frame_end_int_masked(base))
+ rkisp1_mi_isr(stream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void rkisp1_disable_sys_clk(struct rkisp1_device *rkisp1_dev)
+{
+ int i;
+
+ for (i = rkisp1_dev->clk_size - 1; i >= 0; i--)
+ clk_disable_unprepare(rkisp1_dev->clks[i]);
+}
+
+static int rkisp1_enable_sys_clk(struct rkisp1_device *rkisp1_dev)
+{
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < rkisp1_dev->clk_size; i++) {
+ ret = clk_prepare_enable(rkisp1_dev->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+ return 0;
+err:
+ for (--i; i >= 0; --i)
+ clk_disable_unprepare(rkisp1_dev->clks[i]);
+ return ret;
+}
+
+static int rkisp1_plat_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct v4l2_device *v4l2_dev;
+ struct rkisp1_device *isp_dev;
+ const struct isp_match_data *clk_data;
+
+ struct resource *res;
+ int i, ret, irq;
+
+ match = of_match_node(rkisp1_plat_of_match, node);
+ isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
+ if (!isp_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, isp_dev);
+ isp_dev->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ isp_dev->base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(isp_dev->base_addr))
+ return PTR_ERR(isp_dev->base_addr);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rkisp1_irq_handler, IRQF_SHARED,
+ dev_driver_string(dev), dev);
+ if (ret < 0) {
+ dev_err(dev, "request irq failed: %d\n", ret);
+ return ret;
+ }
+
+ isp_dev->irq = irq;
+ clk_data = match->data;
+ for (i = 0; i < clk_data->size; i++) {
+ struct clk *clk = devm_clk_get(dev, clk_data->clks[i]);
+
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get %s\n", clk_data->clks[i]);
+ return PTR_ERR(clk);
+ }
+ isp_dev->clks[i] = clk;
+ }
+ isp_dev->clk_size = clk_data->size;
+
+ atomic_set(&isp_dev->pipe.power_cnt, 0);
+ atomic_set(&isp_dev->pipe.stream_cnt, 0);
+ isp_dev->pipe.open = rkisp1_pipeline_open;
+ isp_dev->pipe.close = rkisp1_pipeline_close;
+ isp_dev->pipe.set_stream = rkisp1_pipeline_set_stream;
+
+ rkisp1_stream_init(isp_dev, RKISP1_STREAM_SP);
+ rkisp1_stream_init(isp_dev, RKISP1_STREAM_MP);
+
+ strlcpy(isp_dev->media_dev.model, "rkisp1",
+ sizeof(isp_dev->media_dev.model));
+ isp_dev->media_dev.dev = &pdev->dev;
+ media_device_init(&isp_dev->media_dev);
+
+ v4l2_dev = &isp_dev->v4l2_dev;
+ v4l2_dev->mdev = &isp_dev->media_dev;
+ strlcpy(v4l2_dev->name, "rkisp1", sizeof(v4l2_dev->name));
+ v4l2_ctrl_handler_init(&isp_dev->ctrl_handler, 5);
+ v4l2_dev->ctrl_handler = &isp_dev->ctrl_handler;
+
+ ret = v4l2_device_register(isp_dev->dev, &isp_dev->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register(&isp_dev->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
+ ret);
+ goto err_unreg_v4l2_dev;
+ }
+
+ /* create & register platefom subdev (from of_node) */
+ ret = rkisp1_register_platform_subdevs(isp_dev);
+ if (ret < 0)
+ goto err_unreg_media_dev;
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err_unreg_media_dev:
+ media_device_unregister(&isp_dev->media_dev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+ return ret;
+}
+
+static int rkisp1_plat_remove(struct platform_device *pdev)
+{
+ struct rkisp1_device *isp_dev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ media_device_unregister(&isp_dev->media_dev);
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+ rkisp1_unregister_params_vdev(&isp_dev->params_vdev);
+ rkisp1_unregister_stats_vdev(&isp_dev->stats_vdev);
+ rkisp1_unregister_stream_vdevs(isp_dev);
+ rkisp1_unregister_isp_subdev(isp_dev);
+
+ return 0;
+}
+
+static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
+{
+ struct rkisp1_device *isp_dev = dev_get_drvdata(dev);
+
+ rkisp1_disable_sys_clk(isp_dev);
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
+{
+ struct rkisp1_device *isp_dev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ return ret;
+ rkisp1_enable_sys_clk(isp_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rkisp1_plat_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
+};
+
+static struct platform_driver rkisp1_plat_drv = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(rkisp1_plat_of_match),
+ .pm = &rkisp1_plat_pm_ops,
+ },
+ .probe = rkisp1_plat_probe,
+ .remove = rkisp1_plat_remove,
+};
+
+module_platform_driver(rkisp1_plat_drv);
+MODULE_AUTHOR("Rockchip Camera/ISP team");
+MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/rockchip/isp1/dev.h b/drivers/media/platform/rockchip/isp1/dev.h
new file mode 100644
index 000000000000..bdedbb5e5550
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/dev.h
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_DEV_H
+#define _RKISP1_DEV_H
+
+#include "capture.h"
+#include "rkisp1.h"
+#include "isp_params.h"
+#include "isp_stats.h"
+
+#define DRIVER_NAME "rkisp1"
+#define ISP_VDEV_NAME DRIVER_NAME "_ispdev"
+#define SP_VDEV_NAME DRIVER_NAME "_selfpath"
+#define MP_VDEV_NAME DRIVER_NAME "_mainpath"
+#define DMA_VDEV_NAME DRIVER_NAME "_dmapath"
+
+#define GRP_ID_SENSOR BIT(0)
+#define GRP_ID_MIPIPHY BIT(1)
+#define GRP_ID_ISP BIT(2)
+#define GRP_ID_ISP_MP BIT(3)
+#define GRP_ID_ISP_SP BIT(4)
+
+#define RKISP1_MAX_BUS_CLK 8
+#define RKISP1_MAX_SENSOR 2
+#define RKISP1_MAX_PIPELINE 4
+
+/*
+ * struct rkisp1_pipeline - An ISP hardware pipeline
+ *
+ * Capture device call other devices via pipeline
+ *
+ * @num_subdevs: number of linked subdevs
+ * @power_cnt: pipeline power count
+ * @stream_cnt: stream power count
+ */
+struct rkisp1_pipeline {
+ struct media_pipeline pipe;
+ int num_subdevs;
+ atomic_t power_cnt;
+ atomic_t stream_cnt;
+ struct v4l2_subdev *subdevs[RKISP1_MAX_PIPELINE];
+ int (*open)(struct rkisp1_pipeline *p,
+ struct media_entity *me, bool prepare);
+ int (*close)(struct rkisp1_pipeline *p);
+ int (*set_stream)(struct rkisp1_pipeline *p, bool on);
+};
+
+/*
+ * struct rkisp1_sensor_info - Sensor information
+ * @mbus: media bus configuration
+ */
+struct rkisp1_sensor_info {
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_config mbus;
+};
+
+/*
+ * struct rkisp1_device - ISP platform device
+ * @base_addr: base register address
+ * @active_sensor: sensor in-use, set when streaming on
+ * @isp_sdev: ISP sub-device
+ * @rkisp1_stream: capture video device
+ * @stats_vdev: ISP statistics output device
+ * @params_vdev: ISP input parameters device
+ */
+struct rkisp1_device {
+ void __iomem *base_addr;
+ int irq;
+ struct device *dev;
+ struct clk *clks[RKISP1_MAX_BUS_CLK];
+ int clk_size;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct media_device media_dev;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *subdevs[RKISP1_SD_MAX];
+ struct rkisp1_sensor_info *active_sensor;
+ struct rkisp1_sensor_info sensors[RKISP1_MAX_SENSOR];
+ int num_sensors;
+ struct rkisp1_isp_subdev isp_sdev;
+ struct rkisp1_stream stream[RKISP1_MAX_STREAM];
+ struct rkisp1_isp_stats_vdev stats_vdev;
+ struct rkisp1_isp_params_vdev params_vdev;
+ struct rkisp1_pipeline pipe;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+#endif
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 10/17] dt-bindings: Document the Rockchip ISP1 bindings
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (7 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
` (11 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add DT bindings documentation for Rockchip ISP1
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/media/rockchip-isp1.txt | 69 ++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
diff --git a/Documentation/devicetree/bindings/media/rockchip-isp1.txt b/Documentation/devicetree/bindings/media/rockchip-isp1.txt
new file mode 100644
index 000000000000..4631a4b7c88a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-isp1.txt
@@ -0,0 +1,69 @@
+Rockchip SoC Image Signal Processing unit v1
+----------------------------------------------
+
+Rockchip ISP1 is the Camera interface for the Rockchip series of SoCs
+which contains image processing, scaling, and compression funcitons.
+
+Required properties:
+- compatible: value should be one of the following
+ "rockchip,rk3288-cif-isp";
+ "rockchip,rk3399-cif-isp";
+- reg : offset and length of the register set for the device.
+- interrupts: should contain ISP interrupt.
+- clocks: phandle to the required clocks.
+- clock-names: required clock name.
+- iommus: required a iommu node.
+
+port node
+-------------------
+
+The device node should contain one 'port' child node with child 'endpoint'
+nodes, according to the bindings defined in Documentation/devicetree/bindings/
+media/video-interfaces.txt.
+
+- endpoint(parallel):
+ - remote-endpoint: Connecting to a sensor with a parallel video bus.
+ - parallel_bus properties: Refer to Documentation/devicetree/bindings/
+ media/video-interfaces.txt.
+- endpoint(mipi):
+ - remote-endpoint: Connecting to Rockchip MIPI-DPHY,
+ which is defined in rockchip-mipi-dphy.txt.
+
+The port node must contain at least one endpoint, either parallel or mipi.
+It could have multiple endpoints, but please note the hardware don't support
+two sensors work at a time, they are supposed to work asynchronously.
+
+Device node example
+-------------------
+
+ isp0: isp0 at ff910000 {
+ compatible = "rockchip,rk3399-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru SCLK_ISP0>,
+ <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>,
+ <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>;
+ clock-names = "clk_isp",
+ "aclk_isp", "aclk_isp_wrap",
+ "hclk_isp", "hclk_isp_wrap";
+ power-domains = <&power RK3399_PD_ISP0>;
+ iommus = <&isp0_mmu>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* mipi */
+ isp0_mipi_in: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&dphy_rx0_out>;
+ };
+
+ /* parallel */
+ isp0_parallel_in: endpoint at 1 {
+ reg = <1>;
+ remote-endpoint = <&ov5640_out>;
+ bus-width = <8>;
+ };
+ };
+ };
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (8 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 10/17] dt-bindings: Document the Rockchip ISP1 bindings Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 12/17] ARM: dts: rockchip: add isp node for rk3288 Jacob Chen
` (10 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add DT bindings documentation for Rockchip MIPI D-PHY RX
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../bindings/media/rockchip-mipi-dphy.txt | 90 ++++++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
diff --git a/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt b/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
new file mode 100644
index 000000000000..d83700faf4c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
@@ -0,0 +1,90 @@
+Rockchip SoC MIPI RX D-PHY
+-------------------------------------------------------------
+
+Required properties:
+- compatible: value should be one of the following
+ "rockchip,rk3288-mipi-dphy"
+ "rockchip,rk3399-mipi-dphy"
+- clocks : list of clock specifiers, corresponding to entries in
+ clock-names property;
+- clock-names: required clock name.
+
+MIPI RX D-PHY use registers in "general register files", it
+should be a child of the GRF.
+MIPI TXRX D-PHY have its own registers, it must have a reg property.
+
+Optional properties:
+- reg: offset and length of the register set for the device.
+- rockchip,grf: MIPI TX1RX1 D-PHY not only has its own register but also
+ the GRF, so it is only necessary for MIPI TX1RX1 D-PHY.
+
+port node
+-------------------
+
+The device node should contain two 'port' child nodes, according to the bindings
+defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+The first port show the sensors connected in this mipi-dphy.
+- endpoint:
+ - remote-endpoint: Linked to a sensor with a MIPI CSI-2 video bus.
+ - data-lanes : (required) an array specifying active physical MIPI-CSI2
+ data input lanes and their mapping to logical lanes; the
+ D-PHY can't reroute lanes, so the array's content should
+ be consecutive and only its length is meaningful.
+
+The port node must contain at least one endpoint. It could have multiple endpoints
+linked to different sensors, but please note that they are not supposed to be
+activated at the same time.
+
+The second port should be connected to isp node.
+- endpoint:
+ - remote-endpoint: Linked to Rockchip ISP1, which is defined
+ in rockchip-isp1.txt.
+
+Device node example
+-------------------
+
+grf: syscon at ff770000 {
+ compatible = "rockchip,rk3288-grf", "syscon", "simple-mfd";
+
+...
+
+ mipi_dphy_rx0: mipi-dphy-rx0 {
+ compatible = "rockchip,rk3399-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDPHY_REF>,
+ <&cru SCLK_DPHY_RX0_CFG>,
+ <&cru PCLK_VIO_GRF>;
+ clock-names = "dphy-ref", "dphy-cfg", "grf";
+ power-domains = <&power RK3399_PD_VIO>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port at 0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_in_wcam: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&wcam_out>;
+ data-lanes = <1 2>;
+ };
+ mipi_in_ucam: endpoint at 1 {
+ reg = <1>;
+ remote-endpoint = <&ucam_out>;
+ data-lanes = <1>;
+ };
+ };
+
+ port at 1 {
+ reg = <1>;
+
+ dphy_rx0_out: endpoint {
+ remote-endpoint = <&isp0_mipi_in>;
+ };
+ };
+ };
+ };
+};
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 12/17] ARM: dts: rockchip: add isp node for rk3288
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (9 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 13/17] ARM: dts: rockchip: add rx0 mipi-phy " Jacob Chen
` (9 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
rk3288 have a Embedded 13M ISP
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index ad6bda85c85a..6c122aaf06a7 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -962,6 +962,23 @@
status = "disabled";
};
+ isp: isp at ff910000 {
+ compatible = "rockchip,rk3288-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru SCLK_ISP>, <&cru ACLK_ISP>,
+ <&cru HCLK_ISP>, <&cru PCLK_ISP_IN>,
+ <&cru SCLK_ISP_JPE>;
+ clock-names = "clk_isp", "aclk_isp",
+ "hclk_isp", "pclk_isp_in",
+ "sclk_isp_jpe";
+ assigned-clocks = <&cru SCLK_ISP>, <&cru SCLK_ISP_JPE>;
+ assigned-clock-rates = <400000000>, <400000000>;
+ power-domains = <&power RK3288_PD_VIO>;
+ iommus = <&isp_mmu>;
+ status = "disabled";
+ };
+
isp_mmu: iommu at ff914000 {
compatible = "rockchip,iommu";
reg = <0x0 0xff914000 0x0 0x100>, <0x0 0xff915000 0x0 0x100>;
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 13/17] ARM: dts: rockchip: add rx0 mipi-phy for rk3288
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (10 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 12/17] ARM: dts: rockchip: add isp node for rk3288 Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 14/17] ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node " Jacob Chen
` (8 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
It's a Designware MIPI D-PHY, used by ISP in rk3288.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 6c122aaf06a7..3a530b72c057 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -864,6 +864,13 @@
status = "disabled";
};
+ mipi_phy_rx0: mipi-phy-rx0 {
+ compatible = "rockchip,rk3288-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDSI_24M>, <&cru PCLK_MIPI_CSI>;
+ clock-names = "dphy-ref", "pclk";
+ status = "disabled";
+ };
+
io_domains: io-domains {
compatible = "rockchip,rk3288-io-voltage-domain";
status = "disabled";
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 14/17] ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (11 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 13/17] ARM: dts: rockchip: add rx0 mipi-phy " Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 15/17] arm64: dts: rockchip: add isp0 node for rk3399 Jacob Chen
` (7 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Wen Nuan <leo.wen@rock-chips.com>
Change-Id: I0b6122b2b34ae0f24f0d4a1111c1bbe6018cac4e
Signed-off-by: Wen Nuan <leo.wen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 3a530b72c057..ed05f3d77358 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -1164,6 +1164,15 @@
};
};
+ mipi_phy_tx1rx1: mipi-phy-tx1rx1 at ff968000 {
+ compatible = "rockchip,rk3288-mipi-dphy";
+ reg = <0x0 0xff968000 0x0 0x4000>;
+ rockchip,grf = <&grf>;
+ clocks = <&cru SCLK_MIPIDSI_24M>, <&cru PCLK_MIPI_CSI>;
+ clock-names = "dphy-ref", "pclk";
+ status = "disabled";
+ };
+
edp: dp at ff970000 {
compatible = "rockchip,rk3288-dp";
reg = <0x0 0xff970000 0x0 0x4000>;
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 15/17] arm64: dts: rockchip: add isp0 node for rk3399
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (12 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 14/17] ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node " Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy " Jacob Chen
` (6 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Shunqian Zheng <zhengsq@rock-chips.com>
rk3399 have two ISP, but we havn't test isp1, so just add isp0 at present.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 2605118d4b4c..14ed016f2fdd 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1614,6 +1614,21 @@
status = "disabled";
};
+ isp0: isp0 at ff910000 {
+ compatible = "rockchip,rk3399-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru SCLK_ISP0>,
+ <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>,
+ <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>;
+ clock-names = "clk_isp",
+ "aclk_isp", "aclk_isp_wrap",
+ "hclk_isp", "hclk_isp_wrap";
+ power-domains = <&power RK3399_PD_ISP0>;
+ iommus = <&isp0_mmu>;
+ status = "disabled";
+ };
+
isp0_mmu: iommu at ff914000 {
compatible = "rockchip,iommu";
reg = <0x0 0xff914000 0x0 0x100>, <0x0 0xff915000 0x0 0x100>;
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy for rk3399
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (13 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 15/17] arm64: dts: rockchip: add isp0 node for rk3399 Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
` (5 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Shunqian Zheng <zhengsq@rock-chips.com>
It's a Designware MIPI D-PHY, used for ISP0 in rk3399.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 14ed016f2fdd..d18ab8f56ef2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1308,6 +1308,16 @@
status = "disabled";
};
+ mipi_dphy_rx0: mipi-dphy-rx0 {
+ compatible = "rockchip,rk3399-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDPHY_REF>,
+ <&cru SCLK_DPHY_RX0_CFG>,
+ <&cru PCLK_VIO_GRF>;
+ clock-names = "dphy-ref", "dphy-cfg", "grf";
+ power-domains = <&power RK3399_PD_VIO>;
+ status = "disabled";
+ };
+
u2phy0: usb2-phy at e450 {
compatible = "rockchip,rk3399-usb2phy";
reg = <0xe450 0x10>;
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (14 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy " Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 10:29 ` [PATCH v6 00/17] Rockchip ISP1 Driver Tomasz Figa
` (4 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add MAINTAINERS entry for the rockchip isp1 driver.
This driver is maintained by rockchip officially and it
will be used for rockchip SoC on all linux-kernel based OS.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4623caf8d72d..7a9ff4fa4592 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11852,6 +11852,16 @@ F: drivers/hid/hid-roccat*
F: include/linux/hid-roccat*
F: Documentation/ABI/*/sysfs-driver-hid-roccat*
+ROCKCHIP ISP V1 DRIVER
+M: Jacob chen <jacob2.chen@rock-chips.com>
+M: Shunqian Zheng <zhengsq@rock-chips.com>
+M: Yichong Zhong <zyc@rock-chips.com>
+L: linux-media at vger.kernel.org
+S: Maintained
+F: drivers/media/platform/rockchip/isp1/
+F: Documentation/devicetree/bindings/media/rockchip-isp1.txt
+F: Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
+
ROCKCHIP RASTER 2D GRAPHIC ACCELERATION UNIT DRIVER
M: Jacob chen <jacob2.chen@rock-chips.com>
L: linux-media at vger.kernel.org
--
2.16.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (15 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
@ 2018-03-08 10:29 ` Tomasz Figa
2018-03-08 12:02 ` Baruch Siach
` (3 subsequent siblings)
20 siblings, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-03-08 10:29 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Mar 8, 2018 at 6:47 PM, Jacob Chen <jacob-chen@iotwrt.com> wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> changes in V6:
> - add mipi txrx phy support
> - remove bool and enum from uapi header
> - add buf_prepare op
> - correct some spelling problems
> - return all queued buffers when starting stream failed
Thanks Jacob.
For anyone planning to review, especially Hans, who pointed it out in
previous version, g_mbus_config is still there and we're working on
replacing it with something less controversial.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (16 preceding siblings ...)
2018-03-08 10:29 ` [PATCH v6 00/17] Rockchip ISP1 Driver Tomasz Figa
@ 2018-03-08 12:02 ` Baruch Siach
2018-03-09 0:53 ` Jacob Chen
[not found] ` <20180308094807.9443-9-jacob-chen@iotwrt.com>
` (2 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Baruch Siach @ 2018-03-08 12:02 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Thu, Mar 08, 2018 at 05:47:50PM +0800, Jacob Chen wrote:
> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399
> SoC.
>
> Wiki Pages:
> http://opensource.rock-chips.com/wiki_Rockchip-isp1
>
> The deprecated g_mbus_config op is not dropped in V6 because i am waiting
> tomasz's patches.
Which tree is this series based on? On top of v4.16-rc4 I get the build
failure below. The V4L2_BUF_TYPE_META_OUTPUT macro, for example, is not even
in media_tree.git.
drivers/media/platform/rockchip/isp1/isp_params.c:1321:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_enum_fmt_meta_out?; did you mean ?vidioc_enum_fmt_meta_cap??
.vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~~
vidioc_enum_fmt_meta_cap
drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: note: (near initialization for ?rkisp1_params_ioctl.vidioc_g_std?)
drivers/media/platform/rockchip/isp1/isp_params.c:1322:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_g_fmt_meta_out?; did you mean ?vidioc_g_fmt_meta_cap??
.vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~
vidioc_g_fmt_meta_cap
drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_s_std?)
drivers/media/platform/rockchip/isp1/isp_params.c:1323:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_s_fmt_meta_out?; did you mean ?vidioc_s_fmt_meta_cap??
.vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~
vidioc_s_fmt_meta_cap
drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_querystd?)
drivers/media/platform/rockchip/isp1/isp_params.c:1324:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_try_fmt_meta_out?; did you mean ?vidioc_try_fmt_meta_cap??
.vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~
vidioc_try_fmt_meta_cap
drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: note: (near initialization for ?rkisp1_params_ioctl.vidioc_enum_input?)
drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_params_init_vb2_queue?:
drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: error: ?V4L2_BUF_TYPE_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_BUF_TYPE_SDR_OUTPUT??
q->type = V4L2_BUF_TYPE_META_OUTPUT;
^~~~~~~~~~~~~~~~~~~~~~~~~
V4L2_BUF_TYPE_SDR_OUTPUT
drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: note: each undeclared identifier is reported only once for each function it appears in
CC drivers/media/platform/rockchip/isp1/mipi_dphy_sy.o
drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_register_params_vdev?:
drivers/media/platform/rockchip/isp1/isp_params.c:1507:43: error: ?V4L2_CAP_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_CAP_VBI_OUTPUT??
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
^~~~~~~~~~~~~~~~~~~~
V4L2_CAP_VBI_OUTPUT
Thanks,
baruch
> Jacob Chen (12):
> media: doc: add document for rkisp1 meta buffer format
> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
> media: rkisp1: add Rockchip ISP1 subdev driver
> media: rkisp1: add ISP1 statistics driver
> media: rkisp1: add ISP1 params driver
> media: rkisp1: add capture device driver
> media: rkisp1: add rockchip isp1 core driver
> dt-bindings: Document the Rockchip ISP1 bindings
> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
> ARM: dts: rockchip: add isp node for rk3288
> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
> MAINTAINERS: add entry for Rockchip ISP1 driver
>
> Jeffy Chen (1):
> media: rkisp1: Add user space ABI definitions
>
> Shunqian Zheng (3):
> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
> arm64: dts: rockchip: add isp0 node for rk3399
> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
>
> Wen Nuan (1):
> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
>
> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
> MAINTAINERS | 10 +
> arch/arm/boot/dts/rk3288.dtsi | 33 +
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
> drivers/media/platform/Kconfig | 10 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/rockchip/isp1/Makefile | 8 +
> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
> drivers/media/platform/rockchip/isp1/common.h | 110 ++
> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
> include/uapi/linux/rkisp1-config.h | 798 +++++++++
> include/uapi/linux/videodev2.h | 4 +
> 29 files changed, 9945 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
> create mode 100644 include/uapi/linux/rkisp1-config.h
>
> --
> 2.16.1
>
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-08 12:02 ` Baruch Siach
@ 2018-03-09 0:53 ` Jacob Chen
2018-03-09 4:09 ` Baruch Siach
0 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-09 0:53 UTC (permalink / raw)
To: linux-arm-kernel
Hi Baruch,
2018-03-08 20:02 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
> Hi Jacob,
>
> On Thu, Mar 08, 2018 at 05:47:50PM +0800, Jacob Chen wrote:
>> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399
>> SoC.
>>
>> Wiki Pages:
>> http://opensource.rock-chips.com/wiki_Rockchip-isp1
>>
>> The deprecated g_mbus_config op is not dropped in V6 because i am waiting
>> tomasz's patches.
>
> Which tree is this series based on? On top of v4.16-rc4 I get the build
> failure below. The V4L2_BUF_TYPE_META_OUTPUT macro, for example, is not even
> in media_tree.git.
>
This series is based on v4.16-rc4 with below patch.
https://patchwork.kernel.org/patch/9792001/
> drivers/media/platform/rockchip/isp1/isp_params.c:1321:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_enum_fmt_meta_out?; did you mean ?vidioc_enum_fmt_meta_cap??
> .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~~
> vidioc_enum_fmt_meta_cap
> drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: note: (near initialization for ?rkisp1_params_ioctl.vidioc_g_std?)
> drivers/media/platform/rockchip/isp1/isp_params.c:1322:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_g_fmt_meta_out?; did you mean ?vidioc_g_fmt_meta_cap??
> .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~
> vidioc_g_fmt_meta_cap
> drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_s_std?)
> drivers/media/platform/rockchip/isp1/isp_params.c:1323:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_s_fmt_meta_out?; did you mean ?vidioc_s_fmt_meta_cap??
> .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~
> vidioc_s_fmt_meta_cap
> drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_querystd?)
> drivers/media/platform/rockchip/isp1/isp_params.c:1324:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_try_fmt_meta_out?; did you mean ?vidioc_try_fmt_meta_cap??
> .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~
> vidioc_try_fmt_meta_cap
> drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: note: (near initialization for ?rkisp1_params_ioctl.vidioc_enum_input?)
> drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_params_init_vb2_queue?:
> drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: error: ?V4L2_BUF_TYPE_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_BUF_TYPE_SDR_OUTPUT??
> q->type = V4L2_BUF_TYPE_META_OUTPUT;
> ^~~~~~~~~~~~~~~~~~~~~~~~~
> V4L2_BUF_TYPE_SDR_OUTPUT
> drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: note: each undeclared identifier is reported only once for each function it appears in
> CC drivers/media/platform/rockchip/isp1/mipi_dphy_sy.o
> drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_register_params_vdev?:
> drivers/media/platform/rockchip/isp1/isp_params.c:1507:43: error: ?V4L2_CAP_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_CAP_VBI_OUTPUT??
> vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
> ^~~~~~~~~~~~~~~~~~~~
> V4L2_CAP_VBI_OUTPUT
>
> Thanks,
> baruch
>
>> Jacob Chen (12):
>> media: doc: add document for rkisp1 meta buffer format
>> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
>> media: rkisp1: add Rockchip ISP1 subdev driver
>> media: rkisp1: add ISP1 statistics driver
>> media: rkisp1: add ISP1 params driver
>> media: rkisp1: add capture device driver
>> media: rkisp1: add rockchip isp1 core driver
>> dt-bindings: Document the Rockchip ISP1 bindings
>> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
>> ARM: dts: rockchip: add isp node for rk3288
>> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
>> MAINTAINERS: add entry for Rockchip ISP1 driver
>>
>> Jeffy Chen (1):
>> media: rkisp1: Add user space ABI definitions
>>
>> Shunqian Zheng (3):
>> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
>> arm64: dts: rockchip: add isp0 node for rk3399
>> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
>>
>> Wen Nuan (1):
>> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
>>
>> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
>> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
>> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
>> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
>> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
>> MAINTAINERS | 10 +
>> arch/arm/boot/dts/rk3288.dtsi | 33 +
>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
>> drivers/media/platform/Kconfig | 10 +
>> drivers/media/platform/Makefile | 1 +
>> drivers/media/platform/rockchip/isp1/Makefile | 8 +
>> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
>> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
>> drivers/media/platform/rockchip/isp1/common.h | 110 ++
>> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
>> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
>> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
>> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
>> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
>> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
>> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
>> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
>> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
>> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
>> include/uapi/linux/rkisp1-config.h | 798 +++++++++
>> include/uapi/linux/videodev2.h | 4 +
>> 29 files changed, 9945 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
>> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
>> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
>> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
>> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
>> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
>> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
>> create mode 100644 include/uapi/linux/rkisp1-config.h
>>
>> --
>> 2.16.1
>>
>
> --
> http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
> =}------------------------------------------------ooO--U--Ooo------------{=
> - baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-09 0:53 ` Jacob Chen
@ 2018-03-09 4:09 ` Baruch Siach
2018-03-09 5:05 ` Jacob Chen
0 siblings, 1 reply; 50+ messages in thread
From: Baruch Siach @ 2018-03-09 4:09 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Fri, Mar 09, 2018 at 08:53:57AM +0800, Jacob Chen wrote:
> 2018-03-08 20:02 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
> > On Thu, Mar 08, 2018 at 05:47:50PM +0800, Jacob Chen wrote:
> >> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399
> >> SoC.
> >>
> >> Wiki Pages:
> >> http://opensource.rock-chips.com/wiki_Rockchip-isp1
> >>
> >> The deprecated g_mbus_config op is not dropped in V6 because i am waiting
> >> tomasz's patches.
> >
> > Which tree is this series based on? On top of v4.16-rc4 I get the build
> > failure below. The V4L2_BUF_TYPE_META_OUTPUT macro, for example, is not even
> > in media_tree.git.
>
> This series is based on v4.16-rc4 with below patch.
> https://patchwork.kernel.org/patch/9792001/
This patch does not apply on v4.16-rc4. I also tried v2 of this patch with the
same result:
https://patchwork.linuxtv.org/patch/44682/
Can you push your series to a public git repo branch?
Thanks,
baruch
> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_enum_fmt_meta_out?; did you mean ?vidioc_enum_fmt_meta_cap??
> > .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~~
> > vidioc_enum_fmt_meta_cap
> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> > .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: note: (near initialization for ?rkisp1_params_ioctl.vidioc_g_std?)
> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_g_fmt_meta_out?; did you mean ?vidioc_g_fmt_meta_cap??
> > .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~
> > vidioc_g_fmt_meta_cap
> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> > .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_s_std?)
> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_s_fmt_meta_out?; did you mean ?vidioc_s_fmt_meta_cap??
> > .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~
> > vidioc_s_fmt_meta_cap
> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> > .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_querystd?)
> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_try_fmt_meta_out?; did you mean ?vidioc_try_fmt_meta_cap??
> > .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~
> > vidioc_try_fmt_meta_cap
> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
> > .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: note: (near initialization for ?rkisp1_params_ioctl.vidioc_enum_input?)
> > drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_params_init_vb2_queue?:
> > drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: error: ?V4L2_BUF_TYPE_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_BUF_TYPE_SDR_OUTPUT??
> > q->type = V4L2_BUF_TYPE_META_OUTPUT;
> > ^~~~~~~~~~~~~~~~~~~~~~~~~
> > V4L2_BUF_TYPE_SDR_OUTPUT
> > drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: note: each undeclared identifier is reported only once for each function it appears in
> > CC drivers/media/platform/rockchip/isp1/mipi_dphy_sy.o
> > drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_register_params_vdev?:
> > drivers/media/platform/rockchip/isp1/isp_params.c:1507:43: error: ?V4L2_CAP_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_CAP_VBI_OUTPUT??
> > vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
> > ^~~~~~~~~~~~~~~~~~~~
> > V4L2_CAP_VBI_OUTPUT
> >
> > Thanks,
> > baruch
> >
> >> Jacob Chen (12):
> >> media: doc: add document for rkisp1 meta buffer format
> >> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
> >> media: rkisp1: add Rockchip ISP1 subdev driver
> >> media: rkisp1: add ISP1 statistics driver
> >> media: rkisp1: add ISP1 params driver
> >> media: rkisp1: add capture device driver
> >> media: rkisp1: add rockchip isp1 core driver
> >> dt-bindings: Document the Rockchip ISP1 bindings
> >> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
> >> ARM: dts: rockchip: add isp node for rk3288
> >> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
> >> MAINTAINERS: add entry for Rockchip ISP1 driver
> >>
> >> Jeffy Chen (1):
> >> media: rkisp1: Add user space ABI definitions
> >>
> >> Shunqian Zheng (3):
> >> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
> >> arm64: dts: rockchip: add isp0 node for rk3399
> >> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
> >>
> >> Wen Nuan (1):
> >> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
> >>
> >> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
> >> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
> >> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
> >> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
> >> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
> >> MAINTAINERS | 10 +
> >> arch/arm/boot/dts/rk3288.dtsi | 33 +
> >> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
> >> drivers/media/platform/Kconfig | 10 +
> >> drivers/media/platform/Makefile | 1 +
> >> drivers/media/platform/rockchip/isp1/Makefile | 8 +
> >> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
> >> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
> >> drivers/media/platform/rockchip/isp1/common.h | 110 ++
> >> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
> >> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
> >> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
> >> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
> >> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
> >> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
> >> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
> >> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> >> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
> >> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
> >> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
> >> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
> >> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
> >> include/uapi/linux/rkisp1-config.h | 798 +++++++++
> >> include/uapi/linux/videodev2.h | 4 +
> >> 29 files changed, 9945 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
> >> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
> >> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> >> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> >> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
> >> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
> >> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
> >> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
> >> create mode 100644 include/uapi/linux/rkisp1-config.h
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-09 4:09 ` Baruch Siach
@ 2018-03-09 5:05 ` Jacob Chen
2018-03-09 5:54 ` Baruch Siach
0 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-03-09 5:05 UTC (permalink / raw)
To: linux-arm-kernel
Hi Baruch,
2018-03-09 12:09 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
> Hi Jacob,
>
> On Fri, Mar 09, 2018 at 08:53:57AM +0800, Jacob Chen wrote:
>> 2018-03-08 20:02 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
>> > On Thu, Mar 08, 2018 at 05:47:50PM +0800, Jacob Chen wrote:
>> >> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399
>> >> SoC.
>> >>
>> >> Wiki Pages:
>> >> http://opensource.rock-chips.com/wiki_Rockchip-isp1
>> >>
>> >> The deprecated g_mbus_config op is not dropped in V6 because i am waiting
>> >> tomasz's patches.
>> >
>> > Which tree is this series based on? On top of v4.16-rc4 I get the build
>> > failure below. The V4L2_BUF_TYPE_META_OUTPUT macro, for example, is not even
>> > in media_tree.git.
>>
>> This series is based on v4.16-rc4 with below patch.
>> https://patchwork.kernel.org/patch/9792001/
>
> This patch does not apply on v4.16-rc4. I also tried v2 of this patch with the
> same result:
>
> https://patchwork.linuxtv.org/patch/44682/
It need resolve merge conflict.
>
> Can you push your series to a public git repo branch?
>
Sure, I have push it to my github.
https://github.com/wzyy2/linux/tree/4.16-rc4
This commit might be a bit of a mess
https://github.com/wzyy2/linux/commit/ff68323c4804adc10f64836ea1be172c54a9d6c6
> Thanks,
> baruch
>
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_enum_fmt_meta_out?; did you mean ?vidioc_enum_fmt_meta_cap??
>> > .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~~
>> > vidioc_enum_fmt_meta_cap
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
>> > .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1321:30: note: (near initialization for ?rkisp1_params_ioctl.vidioc_g_std?)
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_g_fmt_meta_out?; did you mean ?vidioc_g_fmt_meta_cap??
>> > .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~
>> > vidioc_g_fmt_meta_cap
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
>> > .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1322:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_s_std?)
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_s_fmt_meta_out?; did you mean ?vidioc_s_fmt_meta_cap??
>> > .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~
>> > vidioc_s_fmt_meta_cap
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
>> > .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1323:27: note: (near initialization for ?rkisp1_params_ioctl.vidioc_querystd?)
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:3: error: ?const struct v4l2_ioctl_ops? has no member named ?vidioc_try_fmt_meta_out?; did you mean ?vidioc_try_fmt_meta_cap??
>> > .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~
>> > vidioc_try_fmt_meta_cap
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
>> > .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
>> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1324:29: note: (near initialization for ?rkisp1_params_ioctl.vidioc_enum_input?)
>> > drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_params_init_vb2_queue?:
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: error: ?V4L2_BUF_TYPE_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_BUF_TYPE_SDR_OUTPUT??
>> > q->type = V4L2_BUF_TYPE_META_OUTPUT;
>> > ^~~~~~~~~~~~~~~~~~~~~~~~~
>> > V4L2_BUF_TYPE_SDR_OUTPUT
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1462:12: note: each undeclared identifier is reported only once for each function it appears in
>> > CC drivers/media/platform/rockchip/isp1/mipi_dphy_sy.o
>> > drivers/media/platform/rockchip/isp1/isp_params.c: In function ?rkisp1_register_params_vdev?:
>> > drivers/media/platform/rockchip/isp1/isp_params.c:1507:43: error: ?V4L2_CAP_META_OUTPUT? undeclared (first use in this function); did you mean ?V4L2_CAP_VBI_OUTPUT??
>> > vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
>> > ^~~~~~~~~~~~~~~~~~~~
>> > V4L2_CAP_VBI_OUTPUT
>> >
>> > Thanks,
>> > baruch
>> >
>> >> Jacob Chen (12):
>> >> media: doc: add document for rkisp1 meta buffer format
>> >> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
>> >> media: rkisp1: add Rockchip ISP1 subdev driver
>> >> media: rkisp1: add ISP1 statistics driver
>> >> media: rkisp1: add ISP1 params driver
>> >> media: rkisp1: add capture device driver
>> >> media: rkisp1: add rockchip isp1 core driver
>> >> dt-bindings: Document the Rockchip ISP1 bindings
>> >> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
>> >> ARM: dts: rockchip: add isp node for rk3288
>> >> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
>> >> MAINTAINERS: add entry for Rockchip ISP1 driver
>> >>
>> >> Jeffy Chen (1):
>> >> media: rkisp1: Add user space ABI definitions
>> >>
>> >> Shunqian Zheng (3):
>> >> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
>> >> arm64: dts: rockchip: add isp0 node for rk3399
>> >> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
>> >>
>> >> Wen Nuan (1):
>> >> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
>> >>
>> >> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
>> >> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
>> >> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
>> >> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
>> >> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
>> >> MAINTAINERS | 10 +
>> >> arch/arm/boot/dts/rk3288.dtsi | 33 +
>> >> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
>> >> drivers/media/platform/Kconfig | 10 +
>> >> drivers/media/platform/Makefile | 1 +
>> >> drivers/media/platform/rockchip/isp1/Makefile | 8 +
>> >> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
>> >> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
>> >> drivers/media/platform/rockchip/isp1/common.h | 110 ++
>> >> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
>> >> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
>> >> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
>> >> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
>> >> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
>> >> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
>> >> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
>> >> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>> >> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
>> >> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
>> >> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
>> >> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
>> >> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
>> >> include/uapi/linux/rkisp1-config.h | 798 +++++++++
>> >> include/uapi/linux/videodev2.h | 4 +
>> >> 29 files changed, 9945 insertions(+)
>> >> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
>> >> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
>> >> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
>> >> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
>> >> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
>> >> create mode 100644 include/uapi/linux/rkisp1-config.h
>
> --
> http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
> =}------------------------------------------------ooO--U--Ooo------------{=
> - baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-09 5:05 ` Jacob Chen
@ 2018-03-09 5:54 ` Baruch Siach
0 siblings, 0 replies; 50+ messages in thread
From: Baruch Siach @ 2018-03-09 5:54 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Fri, Mar 09, 2018 at 01:05:28PM +0800, Jacob Chen wrote:
> 2018-03-09 12:09 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
> > On Fri, Mar 09, 2018 at 08:53:57AM +0800, Jacob Chen wrote:
> >> 2018-03-08 20:02 GMT+08:00 Baruch Siach <baruch@tkos.co.il>:
> >> > On Thu, Mar 08, 2018 at 05:47:50PM +0800, Jacob Chen wrote:
> >> >> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399
> >> >> SoC.
> >> >>
> >> >> Wiki Pages:
> >> >> http://opensource.rock-chips.com/wiki_Rockchip-isp1
> >> >>
> >> >> The deprecated g_mbus_config op is not dropped in V6 because i am waiting
> >> >> tomasz's patches.
> >> >
> >> > Which tree is this series based on? On top of v4.16-rc4 I get the build
> >> > failure below. The V4L2_BUF_TYPE_META_OUTPUT macro, for example, is not even
> >> > in media_tree.git.
> >>
> >> This series is based on v4.16-rc4 with below patch.
> >> https://patchwork.kernel.org/patch/9792001/
> >
> > This patch does not apply on v4.16-rc4. I also tried v2 of this patch with the
> > same result:
> >
> > https://patchwork.linuxtv.org/patch/44682/
>
> It need resolve merge conflict.
>
> > Can you push your series to a public git repo branch?
>
> Sure, I have push it to my github.
> https://github.com/wzyy2/linux/tree/4.16-rc4
>
> This commit might be a bit of a mess
> https://github.com/wzyy2/linux/commit/ff68323c4804adc10f64836ea1be172c54a9d6c6
Thanks. This is very helpful.
I'm mostly interested in the tinkerboard, so the DT bits in your tree are also
useful.
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions
2018-03-08 9:47 ` [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions Jacob Chen
@ 2018-03-09 13:34 ` Hans Verkuil
2018-04-24 4:28 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 13:34 UTC (permalink / raw)
To: linux-arm-kernel
A quick review:
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jeffy Chen <jeffy.chen@rock-chips.com>
>
> Add the header for userspace
>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> ---
> include/uapi/linux/rkisp1-config.h | 798 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 798 insertions(+)
> create mode 100644 include/uapi/linux/rkisp1-config.h
>
> diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
> new file mode 100644
> index 000000000000..1284eb9fd09d
> --- /dev/null
> +++ b/include/uapi/linux/rkisp1-config.h
> @@ -0,0 +1,798 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +/*
> + * References:
> + * REF_01 - ISP_user_manual, Rev 2.57
So this is internal to Rockchip and not available at all?
That should be mentioned otherwise people will ask you for it :-)
> + */
> +
> +#ifndef _UAPI_RKISP1_CONFIG_H
> +#define _UAPI_RKISP1_CONFIG_H
> +
> +#include <linux/types.h>
> +#include <linux/v4l2-controls.h>
> +
> +#define CIFISP_MODULE_DPCC (1 << 0)
> +#define CIFISP_MODULE_BLS (1 << 1)
> +#define CIFISP_MODULE_SDG (1 << 2)
> +#define CIFISP_MODULE_HST (1 << 3)
> +#define CIFISP_MODULE_LSC (1 << 4)
> +#define CIFISP_MODULE_AWB_GAIN (1 << 5)
> +#define CIFISP_MODULE_FLT (1 << 6)
> +#define CIFISP_MODULE_BDM (1 << 7)
> +#define CIFISP_MODULE_CTK (1 << 8)
> +#define CIFISP_MODULE_GOC (1 << 9)
> +#define CIFISP_MODULE_CPROC (1 << 10)
> +#define CIFISP_MODULE_AFC (1 << 11)
> +#define CIFISP_MODULE_AWB (1 << 12)
> +#define CIFISP_MODULE_IE (1 << 13)
> +#define CIFISP_MODULE_AEC (1 << 14)
> +#define CIFISP_MODULE_WDR (1 << 15)
> +#define CIFISP_MODULE_DPF (1 << 16)
> +#define CIFISP_MODULE_DPF_STRENGTH (1 << 17)
Are these abbreviations (DPCC, BLS, etc) documented somewhere publicly?
If not, can you add a short comment before each define explaining what
the abbreviation means?
I see that these abbreviations are actually defined later on with
struct cifisp_isp_other_cfg. I still think they should also be documented
here@the start of the header.
> +
> +#define CIFISP_CTK_COEFF_MAX 0x100
> +#define CIFISP_CTK_OFFSET_MAX 0x800
> +
> +#define CIFISP_AE_MEAN_MAX 25
> +#define CIFISP_HIST_BIN_N_MAX 16
> +#define CIFISP_AFM_MAX_WINDOWS 3
> +#define CIFISP_DEGAMMA_CURVE_SIZE 17
> +
> +#define CIFISP_BDM_MAX_TH 0xFF
> +
> +/*
> + * Black level compensation
> + */
> +/* maximum value for horizontal start address */
> +#define CIFISP_BLS_START_H_MAX 0x00000FFF
> +/* maximum value for horizontal stop address */
> +#define CIFISP_BLS_STOP_H_MAX 0x00000FFF
> +/* maximum value for vertical start address */
> +#define CIFISP_BLS_START_V_MAX 0x00000FFF
> +/* maximum value for vertical stop address */
> +#define CIFISP_BLS_STOP_V_MAX 0x00000FFF
> +/* maximum is 2^18 = 262144*/
> +#define CIFISP_BLS_SAMPLES_MAX 0x00000012
> +/* maximum value for fixed black level */
> +#define CIFISP_BLS_FIX_SUB_MAX 0x00000FFF
> +/* minimum value for fixed black level */
> +#define CIFISP_BLS_FIX_SUB_MIN 0xFFFFF000
> +/* 13 bit range (signed)*/
> +#define CIFISP_BLS_FIX_MASK 0x00001FFF
> +
> +/*
> + * Automatic white balance measurments
measurments -> measurements
> + */
> +#define CIFISP_AWB_MAX_GRID 1
> +#define CIFISP_AWB_MAX_FRAMES 7
> +
> +/*
> + * Gamma out
> + */
> +/* Maximum number of color samples supported */
> +#define CIFISP_GAMMA_OUT_MAX_SAMPLES 17
> +
> +/*
> + * Lens shade correction
> + */
> +#define CIFISP_LSC_GRAD_TBL_SIZE 8
> +#define CIFISP_LSC_SIZE_TBL_SIZE 8
> +/*
> + * The following matches the tuning process,
> + * not the max capabilities of the chip.
> + * Last value unused.
> + */
> +#define CIFISP_LSC_DATA_TBL_SIZE 290
> +
> +/*
> + * Histogram calculation
> + */
> +/* Last 3 values unused. */
> +#define CIFISP_HISTOGRAM_WEIGHT_GRIDS_SIZE 28
> +
> +/*
> + * Defect Pixel Cluster Correction
> + */
> +#define CIFISP_DPCC_METHODS_MAX 3
> +
> +/*
> + * Denoising pre filter
> + */
> +#define CIFISP_DPF_MAX_NLF_COEFFS 17
> +#define CIFISP_DPF_MAX_SPATIAL_COEFFS 6
> +
> +/*
> + * Measurement types
> + */
> +#define CIFISP_STAT_AWB (1 << 0)
> +#define CIFISP_STAT_AUTOEXP (1 << 1)
> +#define CIFISP_STAT_AFM_FIN (1 << 2)
AFM_FIN could use a short comment explaining what it means.
> +#define CIFISP_STAT_HIST (1 << 3)
> +
> +enum cifisp_histogram_mode {
> + CIFISP_HISTOGRAM_MODE_DISABLE,
> + CIFISP_HISTOGRAM_MODE_RGB_COMBINED,
> + CIFISP_HISTOGRAM_MODE_R_HISTOGRAM,
> + CIFISP_HISTOGRAM_MODE_G_HISTOGRAM,
> + CIFISP_HISTOGRAM_MODE_B_HISTOGRAM,
> + CIFISP_HISTOGRAM_MODE_Y_HISTOGRAM
> +};
> +
> +enum cifisp_awb_mode_type {
> + CIFISP_AWB_MODE_MANUAL,
> + CIFISP_AWB_MODE_RGB,
> + CIFISP_AWB_MODE_YCBCR
> +};
> +
> +enum cifisp_flt_mode {
> + CIFISP_FLT_STATIC_MODE,
> + CIFISP_FLT_DYNAMIC_MODE
> +};
> +
> +/**
> + * enum cifisp_exp_ctrl_autostop - stop modes
> + * @CIFISP_EXP_CTRL_AUTOSTOP_0: continuous measurement
> + * @CIFISP_EXP_CTRL_AUTOSTOP_1: stop measuring after a complete frame
> + */
> +enum cifisp_exp_ctrl_autostop {
> + CIFISP_EXP_CTRL_AUTOSTOP_0 = 0,
> + CIFISP_EXP_CTRL_AUTOSTOP_1 = 1,
> +};
> +
> +/**
> + * enum cifisp_exp_meas_mode - Exposure measure mode
> + * @CIFISP_EXP_MEASURING_MODE_0: Y = 16 + 0.25R + 0.5G + 0.1094B
> + * @CIFISP_EXP_MEASURING_MODE_1: Y = (R + G + B) x (85/256)
> + */
> +enum cifisp_exp_meas_mode {
> + CIFISP_EXP_MEASURING_MODE_0,
> + CIFISP_EXP_MEASURING_MODE_1,
> +};
> +
> +/*---------- PART1: Input Parameters ------------*/
> +
> +struct cifisp_window {
> + __u16 h_offs;
> + __u16 v_offs;
> + __u16 h_size;
> + __u16 v_size;
> +} __attribute__ ((packed));
Missing documentation. Yeah, it will be trivial, but still...
> +
> +/**
> + * struct cifisp_bls_fixed_val - BLS fixed subtraction values
> + *
> + * The values will be subtracted from the sensor
> + * values. Therefore a negative value means addition instead of subtraction!
> + *
> + * @r: Fixed (signed!) subtraction value for Bayer pattern R
> + * @gr: Fixed (signed!) subtraction value for Bayer pattern Gr
> + * @gb: Fixed (signed!) subtraction value for Bayer pattern Gb
> + * @b: Fixed (signed!) subtraction value for Bayer pattern B
> + */
> +struct cifisp_bls_fixed_val {
> + __s16 r;
> + __s16 gr;
> + __s16 gb;
> + __s16 b;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_bls_config - Configuration used by black level subtraction
> + *
> + * @enable_auto: Automatic mode activated means that the measured values
> + * are subtracted. Otherwise the fixed subtraction
> + * values will be subtracted.
> + * @en_windows: enabled window
> + * @bls_window1: Measurement window 1 size
> + * @bls_window2: Measurement window 2 size
> + * @bls_samples: Set amount of measured pixels for each Bayer position
> + * (A, B,C and D) to 2^bls_samples.
> + * @cifisp_bls_fixed_val: Fixed subtraction values
> + */
> +struct cifisp_bls_config {
> + __u8 enable_auto;
> + __u8 en_windows;
> + struct cifisp_window bls_window1;
> + struct cifisp_window bls_window2;
> + __u8 bls_samples;
> + struct cifisp_bls_fixed_val fixed_val;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpcc_methods_config - Methods Configuration used by Defect Pixel Cluster Correction
> + *
> + * @method: Method enable bits
> + * @line_thresh: Line threshold
> + * @line_mad_fac: Line MAD factor
> + * @pg_fac: Peak gradient factor
> + * @rnd_thresh: Rank Neighbor Difference threshold
> + * @rg_fac: Rank gradient factor
> + */
> +struct cifisp_dpcc_methods_config {
> + __u32 method;
> + __u32 line_thresh;
> + __u32 line_mad_fac;
> + __u32 pg_fac;
> + __u32 rnd_thresh;
> + __u32 rg_fac;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpcc_methods_config - Configuration used by Defect Pixel Cluster Correction
> + *
> + * @mode: dpcc output mode
> + * @output_mode: whether use hard coded methods
> + * @set_use: stage1 methods set
> + * @methods: methods config
> + * @ro_limits: rank order limits
> + * @rnd_offs: differential rank offsets for rank neighbor difference
> + */
> +struct cifisp_dpcc_config {
> + __u32 mode;
> + __u32 output_mode;
> + __u32 set_use;
> + struct cifisp_dpcc_methods_config methods[CIFISP_DPCC_METHODS_MAX];
> + __u32 ro_limits;
> + __u32 rnd_offs;
> +} __attribute__ ((packed));
> +
> +struct cifisp_gamma_corr_curve {
> + __u16 gamma_y[CIFISP_DEGAMMA_CURVE_SIZE];
> +} __attribute__ ((packed));
> +
> +struct cifisp_gamma_curve_x_axis_pnts {
> + __u32 gamma_dx0;
> + __u32 gamma_dx1;
> +} __attribute__ ((packed));
These two structs need documentation as well.
> +
> +/**
> + * struct cifisp_gamma_corr_curve - Configuration used by sensor degamma
> + *
> + * @curve_x: gamma curve point definition axis for x
This will lead to warnings when building documentation. Just repeat this
three times for _r, _g and _b.
> + * @xa_pnts: x increments
> + */
> +struct cifisp_sdg_config {
> + struct cifisp_gamma_corr_curve curve_r;
> + struct cifisp_gamma_corr_curve curve_g;
> + struct cifisp_gamma_corr_curve curve_b;
> + struct cifisp_gamma_curve_x_axis_pnts xa_pnts;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_lsc_config - Configuration used by Lens shading correction
> + *
> + * refer to REF_01 for details
Hmm, which is not available. So this really needs some documentation.
> + */
> +struct cifisp_lsc_config {
> + __u32 r_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 gr_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 gb_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 b_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> +
> + __u32 x_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
> + __u32 y_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
> +
> + __u32 x_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
> + __u32 y_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
> + __u16 config_width;
> + __u16 config_height;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_ie_config - Configuration used by image effects
> + *
effect and color_sel are not documented.
> + * @eff_mat_1: 3x3 Matrix Coefficients for Emboss Effect 1
> + * @eff_mat_2: 3x3 Matrix Coefficients for Emboss Effect 2
> + * @eff_mat_3: 3x3 Matrix Coefficients for Emboss 3/Sketch 1
> + * @eff_mat_4: 3x3 Matrix Coefficients for Sketch Effect 2
> + * @eff_mat_5: 3x3 Matrix Coefficients for Sketch Effect 3
> + * @eff_tint: Chrominance increment values of tint (used for sepia effect)
> + */
> +struct cifisp_ie_config {
> + __u16 effect;
> + __u16 color_sel;
> + __u16 eff_mat_1;
> + __u16 eff_mat_2;
> + __u16 eff_mat_3;
> + __u16 eff_mat_4;
> + __u16 eff_mat_5;
> + __u16 eff_tint;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_cproc_config - Configuration used by Color Processing
> + *
> + * @c_out_range: Chrominance pixel clipping range at output. (0 for limit, 1 for full)
> + * @y_in_range: Luminance pixel clipping range at output.
> + * @y_out_range: Luminance pixel clipping range at output.
> + * @contrast: 00~ff, 0.0~1.992
> + * @brightness: 80~7F, -128~+127
> + * @sat: saturation, 00~FF, 0.0~1.992
> + * @hue: 80~7F, -90~+87.188
> + */
> +struct cifisp_cproc_config {
> + __u8 c_out_range;
> + __u8 y_in_range;
> + __u8 y_out_range;
> + __u8 contrast;
> + __u8 brightness;
> + __u8 sat;
> + __u8 hue;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_awb_meas_config - Configuration used by auto white balance
> + *
> + * @awb_wnd: white balance measurement window (in pixels) (from enum cifisp_awb_mode_type)
> + * @max_y: only pixels values < max_y contribute to awb measurement, set to 0 to disable this feature
> + * @min_y: only pixels values > min_y contribute to awb measurement
> + * @max_csum: Chrominance sum maximum value, only consider pixels with Cb+Cr,
> + * smaller than threshold for awb measurements
> + * @min_c: Chrominance minimum value, only consider pixels with Cb/Cr
> + * each greater than threshold value for awb measurements
> + * @frames: number of frames - 1 used for mean value calculation (ucFrames=0 means 1 Frame)
> + * @awb_ref_cr: reference Cr value for AWB regulation, target for AWB
> + * @awb_ref_cb: reference Cb value for AWB regulation, target for AWB
> + */
> +struct cifisp_awb_meas_config {
> + /*
> + * Note: currently the h and v offsets are mapped to grid offsets
> + */
> + struct cifisp_window awb_wnd;
> + __u32 awb_mode;
> + __u8 max_y;
> + __u8 min_y;
> + __u8 max_csum;
> + __u8 min_c;
> + __u8 frames;
> + __u8 awb_ref_cr;
> + __u8 awb_ref_cb;
> + __u8 enable_ymax_cmp;
This field is not documented.
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_awb_gain_config - Configuration used by auto white balance gain
> + *
Missing documentation for the fields.
> + * out_data_x = ( AWB_GEAIN_X * in_data + 128) >> 8
> + */
> +struct cifisp_awb_gain_config {
> + __u16 gain_red;
> + __u16 gain_green_r;
> + __u16 gain_blue;
> + __u16 gain_green_b;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_flt_config - Configuration used by ISP filtering
> + *
> + * @mode: ISP_FILT_MODE register fields (from enum cifisp_flt_mode)
> + * @grn_stage1: ISP_FILT_MODE register fields
> + * @chr_h_mode: ISP_FILT_MODE register fields
> + * @chr_v_mode: ISP_FILT_MODE register fields
Is the description for these last three fields correct? It kind of looks
like a copy-and-paste thing.
> + *
> + * refer to REF_01 for details.
Which is not available. I think you should document something here.
> + */
> +
> +struct cifisp_flt_config {
> + __u32 mode;
> + __u8 grn_stage1;
> + __u8 chr_h_mode;
> + __u8 chr_v_mode;
> + __u32 thresh_bl0;
> + __u32 thresh_bl1;
> + __u32 thresh_sh0;
> + __u32 thresh_sh1;
> + __u32 lum_weight;
> + __u32 fac_sh1;
> + __u32 fac_sh0;
> + __u32 fac_mid;
> + __u32 fac_bl0;
> + __u32 fac_bl1;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_bdm_config - Configuration used by Bayer DeMosaic
> + *
> + * @demosaic_th: threshod for bayer demosaicing texture detection
> + */
> +struct cifisp_bdm_config {
> + __u8 demosaic_th;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_ctk_config - Configuration used by Cross Talk correction
> + *
> + * @coeff: color correction matrix
Wouldn't this be done better as __u16 coeff[3][3]?
I assume this is a 3x3 matrix?
ct_offset can be __u16 ct_offset[3].
> + * @ct_offset_b: offset for the crosstalk correction matrix
> + */
> +struct cifisp_ctk_config {
> + __u16 coeff0;
> + __u16 coeff1;
> + __u16 coeff2;
> + __u16 coeff3;
> + __u16 coeff4;
> + __u16 coeff5;
> + __u16 coeff6;
> + __u16 coeff7;
> + __u16 coeff8;
> + __u16 ct_offset_r;
> + __u16 ct_offset_g;
> + __u16 ct_offset_b;
> +} __attribute__ ((packed));
> +
> +enum cifisp_goc_mode {
> + CIFISP_GOC_MODE_LOGARITHMIC,
> + CIFISP_GOC_MODE_EQUIDISTANT
> +};
> +
> +/**
> + * struct cifisp_goc_config - Configuration used by Gamma Out correction
> + *
> + * @mode: goc mode (from enum cifisp_goc_mode)
> + * @gamma_y: gamma out curve y-axis for all color components
> + */
> +struct cifisp_goc_config {
> + __u32 mode;
> + __u16 gamma_y[CIFISP_GAMMA_OUT_MAX_SAMPLES];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_hst_config - Configuration used by Histogram
> + *
> + * @mode: histogram mode (from enum cifisp_histogram_mode)
> + * @histogram_predivider: process every stepsize pixel, all other pixels are skipped
> + * @meas_window: coordinates of the measure window
> + * @hist_weight: weighting factor for sub-windows
> + */
> +struct cifisp_hst_config {
> + __u32 mode;
> + __u8 histogram_predivider;
> + struct cifisp_window meas_window;
> + __u8 hist_weight[CIFISP_HISTOGRAM_WEIGHT_GRIDS_SIZE];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_aec_config - Configuration used by Auto Exposure Control
> + *
> + * @mode: Exposure measure mode (from enum cifisp_exp_meas_mode)
> + * @autostop: stop mode (from enum cifisp_exp_ctrl_autostop)
> + * @meas_window: coordinates of the measure window
> + */
> +struct cifisp_aec_config {
> + __u32 mode;
> + __u32 autostop;
> + struct cifisp_window meas_window;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_afc_config - Configuration used by Auto Focus Control
> + *
> + * @num_afm_win: max CIFISP_AFM_MAX_WINDOWS
> + * @afm_win: coordinates of the meas window
> + * @thres: threshold used for minimizing the influence of noise
> + * @var_shift: the number of bits for the shift operation at the end of the calculation chain.
> + */
> +struct cifisp_afc_config {
> + __u8 num_afm_win;
> + struct cifisp_window afm_win[CIFISP_AFM_MAX_WINDOWS];
> + __u32 thres;
> + __u32 var_shift;
> +} __attribute__ ((packed));
> +
> +/**
> + * enum cifisp_dpf_gain_usage - dpf gain usage
> + * @CIFISP_DPF_GAIN_USAGE_DISABLED: don't use any gains in preprocessing stage
> + * @CIFISP_DPF_GAIN_USAGE_NF_GAINS: use only the noise function gains from registers DPF_NF_GAIN_R, ...
> + * @CIFISP_DPF_GAIN_USAGE_LSC_GAINS: use only the gains from LSC module
> + * @CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS: use the noise function gains and the gains from LSC module
> + * @CIFISP_DPF_GAIN_USAGE_AWB_GAINS: use only the gains from AWB module
> + * @CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS: use the gains from AWB and LSC module
> + * @CIFISP_DPF_GAIN_USAGE_MAX: upper border (only for an internal evaluation)
> + */
> +enum cifisp_dpf_gain_usage {
> + CIFISP_DPF_GAIN_USAGE_DISABLED,
> + CIFISP_DPF_GAIN_USAGE_NF_GAINS,
> + CIFISP_DPF_GAIN_USAGE_LSC_GAINS,
> + CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS,
> + CIFISP_DPF_GAIN_USAGE_AWB_GAINS,
> + CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS,
> + CIFISP_DPF_GAIN_USAGE_MAX
> +};
> +
> +/**
> + * enum cifisp_dpf_gain_usage - dpf gain usage
> + * @CIFISP_DPF_RB_FILTERSIZE_13x9: red and blue filter kernel size 13x9 (means 7x5 active pixel)
> + * @CIFISP_DPF_RB_FILTERSIZE_9x9: red and blue filter kernel size 9x9 (means 5x5 active pixel)
> + */
> +enum cifisp_dpf_rb_filtersize {
> + CIFISP_DPF_RB_FILTERSIZE_13x9,
> + CIFISP_DPF_RB_FILTERSIZE_9x9,
> +};
> +
> +/**
> + * enum cifisp_dpf_nll_scale_mode - dpf noise level scale mode
> + * @CIFISP_NLL_SCALE_LINEAR: use a linear scaling
> + * @CIFISP_NLL_SCALE_LOGARITHMIC: use a logarithmic scaling
> + */
> +enum cifisp_dpf_nll_scale_mode {
> + CIFISP_NLL_SCALE_LINEAR,
> + CIFISP_NLL_SCALE_LOGARITHMIC,
> +};
> +
> +/**
> + * struct cifisp_dpf_nll - Noise level lookup
> + *
> + * @coeff: Noise level Lookup coefficient
> + * @scale_mode: dpf noise level scale mode (from enum cifisp_dpf_nll_scale_mode)
> + */
> +struct cifisp_dpf_nll {
> + __u16 coeff[CIFISP_DPF_MAX_NLF_COEFFS];
> + __u32 scale_mode;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpf_rb_flt - Red blue filter config
> + *
> + * @fltsize: The filter size for the red and blue pixels
> + * (from enum cifisp_dpf_rb_filtersize)
> + * @spatial_coeff: Spatial weights
> + * @r_enable: enable filter processing for red pixels
> + * @b_enable: enable filter processing for blue pixels
> + */
> +struct cifisp_dpf_rb_flt {
> + __u32 fltsize;
> + __u8 spatial_coeff[CIFISP_DPF_MAX_SPATIAL_COEFFS];
> + __u8 r_enable;
> + __u8 b_enable;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpf_g_flt - Green filter Configuration
> + *
> + * @spatial_coeff: Spatial weights
> + * @gr_enable: enable filter processing for green pixels in green/red lines
> + * @gb_enable: enable filter processing for green pixels in green/blue lines
> + */
> +struct cifisp_dpf_g_flt {
> + __u8 spatial_coeff[CIFISP_DPF_MAX_SPATIAL_COEFFS];
> + __u8 gr_enable;
> + __u8 gb_enable;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpf_gain - Noise function Configuration
> + *
> + * @mode: dpf gain usage (from enum cifisp_dpf_gain_usage)
> + * @nf_r_gain: Noise function Gain that replaces the AWB gain for red pixels
> + * @nf_b_gain: Noise function Gain that replaces the AWB gain for blue pixels
> + * @nf_gr_gain: Noise function Gain that replaces the AWB gain
> + * for green pixels in a red line
> + * @nf_gb_gain: Noise function Gain that replaces the AWB gain
> + * for green pixels in a blue line
> + */
> +struct cifisp_dpf_gain {
> + __u32 mode;
> + __u16 nf_r_gain;
> + __u16 nf_b_gain;
> + __u16 nf_gr_gain;
> + __u16 nf_gb_gain;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpf_config - Configuration used by De-noising pre-filter
> + *
> + * @gain: noise function gain
> + * @g_flt: green filter config
> + * @rb_flt: red blue filter config
> + * @nll: noise level lookup
> + */
> +struct cifisp_dpf_config {
> + struct cifisp_dpf_gain gain;
> + struct cifisp_dpf_g_flt g_flt;
> + struct cifisp_dpf_rb_flt rb_flt;
> + struct cifisp_dpf_nll nll;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_dpf_strength_config - strength of the filter
> + *
> + * @r: filter strength of the RED filter
> + * @g: filter strength of the GREEN filter
> + * @b: filter strength of the BLUE filter
> + */
> +struct cifisp_dpf_strength_config {
> + __u8 r;
> + __u8 g;
> + __u8 b;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_isp_other_cfg - Parameters for some blocks in rockchip isp1
> + *
> + * @dpcc_config: Defect Pixel Cluster Correction config
> + * @bls_config: Black Level Subtraction config
> + * @sdg_config: sensor degamma config
> + * @lsc_config: Lens Shade config
> + * @awb_gain_config: Auto White balance gain config
> + * @flt_config: filter config
> + * @bdm_config: demosaic config
> + * @ctk_config: cross talk config
> + * @goc_config: gamma out config
> + * @bls_config: black level subtraction config
> + * @dpf_config: De-noising pre-filter config
> + * @dpf_strength_config: dpf strength config
> + * @cproc_config: color process config
> + * @ie_config: image effects config
> + */
> +struct cifisp_isp_other_cfg {
> + struct cifisp_dpcc_config dpcc_config;
> + struct cifisp_bls_config bls_config;
> + struct cifisp_sdg_config sdg_config;
> + struct cifisp_lsc_config lsc_config;
> + struct cifisp_awb_gain_config awb_gain_config;
> + struct cifisp_flt_config flt_config;
> + struct cifisp_bdm_config bdm_config;
> + struct cifisp_ctk_config ctk_config;
> + struct cifisp_goc_config goc_config;
> + struct cifisp_dpf_config dpf_config;
> + struct cifisp_dpf_strength_config dpf_strength_config;
> + struct cifisp_cproc_config cproc_config;
> + struct cifisp_ie_config ie_config;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_isp_meas_cfg - Rockchip ISP1 Measure Parameters
> + *
> + * @awb_meas_config: auto white balance config
> + * @hst_config: histogram config
> + * @aec_config: auto exposure config
> + * @afc_config: auto focus config
> + */
> +struct cifisp_isp_meas_cfg {
> + struct cifisp_awb_meas_config awb_meas_config;
> + struct cifisp_hst_config hst_config;
> + struct cifisp_aec_config aec_config;
> + struct cifisp_afc_config afc_config;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct rkisp1_isp_params_cfg - Rockchip ISP1 Input Parameters Meta Data
> + *
> + * @module_en_update: mask the enable bits of which module should be updated
> + * @module_ens: mask the enable value of each module, only update the module
> + * which correspond bit was set in module_en_update
> + * @module_cfg_update: mask the config bits of which module should be updated
> + * @meas: measurement config
> + * @others: other config
> + */
> +struct rkisp1_isp_params_cfg {
> + __u32 module_en_update;
> + __u32 module_ens;
> + __u32 module_cfg_update;
> +
> + struct cifisp_isp_meas_cfg meas;
> + struct cifisp_isp_other_cfg others;
> +} __attribute__ ((packed));
> +
> +/*---------- PART2: Measurement Statistics ------------*/
> +
> +/**
> + * struct cifisp_bls_meas_val - AWB measured values
> + *
> + * @cnt: White pixel count, number of "white pixels" found during laster measurement
> + * @mean_y_or_g: Mean value of Y within window and frames, Green if RGB is selected.
> + * @mean_cb_or_b: Mean value of Cb within window and frames, Blue if RGB is selected.
> + * @mean_cr_or_r: Mean value of Cr within window and frames, Red if RGB is selected.
> + */
> +struct cifisp_awb_meas {
> + __u32 cnt;
> + __u8 mean_y_or_g;
> + __u8 mean_cb_or_b;
> + __u8 mean_cr_or_r;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_awb_stat - statistics automatic white balance data
> + *
> + * @awb_mean: Mean measured data
> + */
> +struct cifisp_awb_stat {
> + struct cifisp_awb_meas awb_mean[CIFISP_AWB_MAX_GRID];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_bls_meas_val - BLS measured values
> + *
> + * @meas_r: Mean measured value for Bayer pattern R
> + * @meas_gr: Mean measured value for Bayer pattern Gr
> + * @meas_gb: Mean measured value for Bayer pattern Gb
> + * @meas_b: Mean measured value for Bayer pattern B
> + */
> +struct cifisp_bls_meas_val {
> + __u16 meas_r;
> + __u16 meas_gr;
> + __u16 meas_gb;
> + __u16 meas_b;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_ae_stat - statistics auto exposure data
> + *
> + * @exp_mean: Mean luminance value of block xx
> + * @bls_val: BLS measured values
> + *
> + * Image is divided into 5x5 blocks.
> + */
> +struct cifisp_ae_stat {
> + __u8 exp_mean[CIFISP_AE_MEAN_MAX];
> + struct cifisp_bls_meas_val bls_val;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_af_meas_val - AF measured values
> + *
> + * @sum: sharpness, refer to REF_01 for definition
> + * @lum: luminance, refer to REF_01 for definition
> + */
> +struct cifisp_af_meas_val {
> + __u32 sum;
> + __u32 lum;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_af_stat - statistics auto focus data
> + *
> + * @window: AF measured value of window x
> + *
> + * The module measures the sharpness in 3 windows of selectable size via
> + * register settings(ISP_AFM_*_A/B/C)
> + */
> +struct cifisp_af_stat {
> + struct cifisp_af_meas_val window[CIFISP_AFM_MAX_WINDOWS];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_hist_stat - statistics histogram data
> + *
> + * @hist_bins: measured bin counters
> + *
> + * Measurement window divided into 25 sub-windows, set
> + * with ISP_HIST_XXX
> + */
> +struct cifisp_hist_stat {
> + __u16 hist_bins[CIFISP_HIST_BIN_N_MAX];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct rkisp1_stat_buffer - Rockchip ISP1 Statistics Data
> + *
> + * @cifisp_awb_stat: statistics data for automatic white balance
> + * @cifisp_ae_stat: statistics data for auto exposure
> + * @cifisp_af_stat: statistics data for auto focus
> + * @cifisp_hist_stat: statistics histogram data
> + */
> +struct cifisp_stat {
> + struct cifisp_awb_stat awb;
> + struct cifisp_ae_stat ae;
> + struct cifisp_af_stat af;
> + struct cifisp_hist_stat hist;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct rkisp1_stat_buffer - Rockchip ISP1 Statistics Meta Data
> + *
> + * @meas_type: measurement types (CIFISP_STAT_ definitions)
> + * @frame_id: frame ID for sync
> + * @params: statistics data
> + */
> +struct rkisp1_stat_buffer {
> + __u32 meas_type;
> + __u32 frame_id;
> + struct cifisp_stat params;
> +} __attribute__ ((packed));
> +
> +#endif /* _UAPI_RKISP1_CONFIG_H */
>
Notwithstanding my comments, this is actually looking quite good. My apologies for
being so thorough in my review. But that's a consequence of the datasheet being
unavailable to the users of this API.
Note: please mention as well that this can all be zeroed and it will still work.
And that users only have to change those module configs in which they are
interested.
Final question: can you perhaps show what the capture looks like if you initialize
this to 0? Is it acceptable?
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 02/17] media: doc: add document for rkisp1 meta buffer format
2018-03-08 9:47 ` [PATCH v6 02/17] media: doc: add document for " Jacob Chen
@ 2018-03-09 13:37 ` Hans Verkuil
0 siblings, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 13:37 UTC (permalink / raw)
To: linux-arm-kernel
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This commit add docuemnt for rkisp1 meta buffer format
>
> Signed-off-by: Jacob Chen <jacob-chen@rock-chips.com>
> Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
> Documentation/media/uapi/v4l/meta-formats.rst | 2 ++
> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 ++++++++++++++++++++
> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 ++++++++++++++++++
> 3 files changed, 40 insertions(+)
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
>
> diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst
> index 0c4e1ecf5879..44e7edbf1dae 100644
> --- a/Documentation/media/uapi/v4l/meta-formats.rst
> +++ b/Documentation/media/uapi/v4l/meta-formats.rst
> @@ -15,3 +15,5 @@ These formats are used for the :ref:`metadata` interface only.
> pixfmt-meta-uvc
> pixfmt-meta-vsp1-hgo
> pixfmt-meta-vsp1-hgt
> + pixfmt-meta-rkisp1-params
> + pixfmt-meta-rkisp1-stat
> diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> new file mode 100644
> index 000000000000..2ff4f4309795
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> @@ -0,0 +1,20 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _v4l2-meta-fmt-rkisp1-params:
> +
> +*******************************
> +V4L2_META_FMT_RK_ISP1_PARAMS
> +*******************************
> +
> +Rockchip ISP1 Parameters Data
> +
> +Description
> +===========
> +
> +This format describes input parameters for the Rockchip ISP1.
> +
> +It uses c-struct :c:type:`rkisp1_isp_params_cfg`, which is defined in
> +the ``linux/rkisp1-config.h`` header file, see it for details.
> +
> +The parameters consist of multiple modules.
> +The module won't be updated if the correspond bit was not set in module_*_update.
correspond -> corresponding
> diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> new file mode 100644
> index 000000000000..dca8befe58f1
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> @@ -0,0 +1,18 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _v4l2-meta-fmt-rkisp1-stat:
> +
> +*******************************
> +V4L2_META_FMT_RK_ISP1_STAT_3A
> +*******************************
> +
> +Rockchip ISP1 Statistics Data
> +
> +Description
> +===========
> +
> +This format describes image color statistics information generated by the Rockchip
> +ISP1.
> +
> +It use c-struct :c:type:`rkisp1_stat_buffer`, which is defined in
use -> uses
> +the ``linux/cifisp_stat.h`` header file, see it for details.
>
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
@ 2018-03-09 13:57 ` Hans Verkuil
2018-05-03 9:09 ` Baruch Siach
1 sibling, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 13:57 UTC (permalink / raw)
To: linux-arm-kernel
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> Add the subdev driver for rockchip isp1.
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
> Signed-off-by: Jacob Chen <cc@rock-chips.com>
> Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 +++
> 2 files changed, 1282 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.c b/drivers/media/platform/rockchip/isp1/rkisp1.c
> new file mode 100644
> index 000000000000..bb16c8118c16
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
> @@ -0,0 +1,1177 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/iopoll.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/videodev2.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-event.h>
> +
> +#include "common.h"
> +#include "regs.h"
> +
> +#define CIF_ISP_INPUT_W_MAX 4032
> +#define CIF_ISP_INPUT_H_MAX 3024
> +#define CIF_ISP_INPUT_W_MIN 32
> +#define CIF_ISP_INPUT_H_MIN 32
> +#define CIF_ISP_OUTPUT_W_MAX CIF_ISP_INPUT_W_MAX
> +#define CIF_ISP_OUTPUT_H_MAX CIF_ISP_INPUT_H_MAX
> +#define CIF_ISP_OUTPUT_W_MIN CIF_ISP_INPUT_W_MIN
> +#define CIF_ISP_OUTPUT_H_MIN CIF_ISP_INPUT_H_MIN
> +
> +/*
> + * NOTE: MIPI controller and input MUX are also configured in this file,
> + * because ISP Subdev is not only describe ISP submodule(input size,format, output size, format),
> + * but also a virtual route device.
> + */
> +
> +/*
> + * There are many variables named with format/frame in below code,
> + * please see here for their meaning.
> + *
> + * Cropping regions of ISP
> + *
> + * +---------------------------------------------------------+
> + * | Sensor image |
> + * | +---------------------------------------------------+ |
> + * | | ISP_ACQ (for black level) | |
> + * | | in_frm | |
> + * | | +--------------------------------------------+ | |
> + * | | | ISP_OUT | | |
> + * | | | in_crop | | |
> + * | | | +---------------------------------+ | | |
> + * | | | | ISP_IS | | | |
> + * | | | | rkisp1_isp_subdev: out_crop | | | |
> + * | | | +---------------------------------+ | | |
> + * | | +--------------------------------------------+ | |
> + * | +---------------------------------------------------+ |
> + * +---------------------------------------------------------+
> + */
> +
> +static inline struct rkisp1_device *sd_to_isp_dev(struct v4l2_subdev *sd)
> +{
> + return container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev);
> +}
> +
> +/* Get sensor by enabled media link */
> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> +{
> + struct media_pad *local;
> + struct media_entity *sensor_me;
> +
> + local = &sd->entity.pads[RKISP1_ISP_PAD_SINK];
> + sensor_me = media_entity_remote_pad(local)->entity;
> +
> + return media_entity_to_v4l2_subdev(sensor_me);
> +}
> +
> +static struct rkisp1_sensor_info *sd_to_sensor(struct rkisp1_device *dev,
> + struct v4l2_subdev *sd)
> +{
> + int i;
> +
> + for (i = 0; i < dev->num_sensors; ++i)
> + if (dev->sensors[i].sd == sd)
> + return &dev->sensors[i];
> +
> + return NULL;
> +}
> +
> +/**************** register operations ****************/
> +
> +/*
> + * Image Stabilization.
> + * This should only be called when configuring CIF
> + * or at the frame end interrupt
> + */
> +static void rkisp1_config_ism(struct rkisp1_device *dev)
> +{
> + void __iomem *base = dev->base_addr;
> + struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
> + u32 val;
> +
> + writel(0, base + CIF_ISP_IS_RECENTER);
> + writel(0, base + CIF_ISP_IS_MAX_DX);
> + writel(0, base + CIF_ISP_IS_MAX_DY);
> + writel(0, base + CIF_ISP_IS_DISPLACE);
> + writel(out_crop->left, base + CIF_ISP_IS_H_OFFS);
> + writel(out_crop->top, base + CIF_ISP_IS_V_OFFS);
> + writel(out_crop->width, base + CIF_ISP_IS_H_SIZE);
> + writel(out_crop->height, base + CIF_ISP_IS_V_SIZE);
> +
> + /* IS(Image Stabilization) is always on, working as output crop */
> + writel(1, base + CIF_ISP_IS_CTRL);
> + val = readl(base + CIF_ISP_CTRL);
> + val |= CIF_ISP_CTRL_ISP_CFG_UPD;
> + writel(val, base + CIF_ISP_CTRL);
> +}
> +
> +/*
> + * configure isp blocks with input format, size......
> + */
> +static int rkisp1_config_isp(struct rkisp1_device *dev)
> +{
> + struct ispsd_in_fmt *in_fmt;
> + struct ispsd_out_fmt *out_fmt;
> + struct v4l2_mbus_framefmt *in_frm;
> + struct v4l2_rect *out_crop, *in_crop;
> + struct rkisp1_sensor_info *sensor;
> + void __iomem *base = dev->base_addr;
> + u32 isp_ctrl = 0;
> + u32 irq_mask = 0;
> + u32 signal = 0;
> + u32 acq_mult = 0;
> +
> + sensor = dev->active_sensor;
> + in_frm = &dev->isp_sdev.in_frm;
> + in_fmt = &dev->isp_sdev.in_fmt;
> + out_fmt = &dev->isp_sdev.out_fmt;
> + out_crop = &dev->isp_sdev.out_crop;
> + in_crop = &dev->isp_sdev.in_crop;
> +
> + if (in_fmt->fmt_type == FMT_BAYER) {
> + acq_mult = 1;
> + if (out_fmt->fmt_type == FMT_BAYER) {
> + if (sensor->mbus.type == V4L2_MBUS_BT656)
> + isp_ctrl =
> + CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
> + else
> + isp_ctrl =
> + CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
> + } else {
> + writel(CIF_ISP_DEMOSAIC_TH(0xc),
> + base + CIF_ISP_DEMOSAIC);
> +
> + if (sensor->mbus.type == V4L2_MBUS_BT656)
> + isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
> + else
> + isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
> + }
> + } else if (in_fmt->fmt_type == FMT_YUV) {
> + acq_mult = 2;
> + if (sensor->mbus.type == V4L2_MBUS_CSI2) {
> + isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
> + } else {
> + if (sensor->mbus.type == V4L2_MBUS_BT656)
> + isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU656;
> + else
> + isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
> +
> + }
> +
> + irq_mask |= CIF_ISP_DATA_LOSS;
> + }
> +
> + /* Set up input acquisition properties */
> + if (sensor->mbus.type == V4L2_MBUS_BT656 ||
> + sensor->mbus.type == V4L2_MBUS_PARALLEL) {
> + if (sensor->mbus.flags &
> + V4L2_MBUS_PCLK_SAMPLE_RISING)
> + signal = CIF_ISP_ACQ_PROP_POS_EDGE;
> + }
> +
> + if (sensor->mbus.type == V4L2_MBUS_PARALLEL) {
> + if (sensor->mbus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
> + signal |= CIF_ISP_ACQ_PROP_VSYNC_LOW;
> +
> + if (sensor->mbus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
> + signal |= CIF_ISP_ACQ_PROP_HSYNC_LOW;
> + }
> +
> + writel(isp_ctrl, base + CIF_ISP_CTRL);
> + writel(signal | in_fmt->yuv_seq |
> + CIF_ISP_ACQ_PROP_BAYER_PAT(in_fmt->bayer_pat) |
> + CIF_ISP_ACQ_PROP_FIELD_SEL_ALL, base + CIF_ISP_ACQ_PROP);
> + writel(0, base + CIF_ISP_ACQ_NR_FRAMES);
> +
> + /* Acquisition Size */
> + writel(0, base + CIF_ISP_ACQ_H_OFFS);
> + writel(0, base + CIF_ISP_ACQ_V_OFFS);
> + writel(acq_mult * in_frm->width, base + CIF_ISP_ACQ_H_SIZE);
> + writel(in_frm->height, base + CIF_ISP_ACQ_V_SIZE);
> +
> + /* ISP Out Area */
> + writel(in_crop->left, base + CIF_ISP_OUT_H_OFFS);
> + writel(in_crop->top, base + CIF_ISP_OUT_V_OFFS);
> + writel(in_crop->width, base + CIF_ISP_OUT_H_SIZE);
> + writel(in_crop->height, base + CIF_ISP_OUT_V_SIZE);
> +
> + /* interrupt mask */
> + irq_mask |= CIF_ISP_FRAME | CIF_ISP_V_START | CIF_ISP_PIC_SIZE_ERROR |
> + CIF_ISP_FRAME_IN;
> + writel(irq_mask, base + CIF_ISP_IMSC);
> +
> + if (out_fmt->fmt_type == FMT_BAYER)
> + rkisp1_params_disable_isp(&dev->params_vdev);
> + else
> + rkisp1_params_configure_isp(&dev->params_vdev, in_fmt,
> + dev->isp_sdev.quantization);
> +
> + return 0;
> +}
> +
> +static int rkisp1_config_dvp(struct rkisp1_device *dev)
> +{
> + struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
> + void __iomem *base = dev->base_addr;
> + u32 val, input_sel;
> +
> + switch (in_fmt->bus_width) {
> + case 8:
> + input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
> + break;
> + case 10:
> + input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
> + break;
> + case 12:
> + input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B;
> + break;
> + default:
> + v4l2_err(&dev->v4l2_dev, "Invalid bus width\n");
> + return -EINVAL;
> + }
> +
> + val = readl(base + CIF_ISP_ACQ_PROP);
> + writel(val | input_sel, base + CIF_ISP_ACQ_PROP);
> +
> + return 0;
> +}
> +
> +static int rkisp1_config_mipi(struct rkisp1_device *dev)
> +{
> + u32 mipi_ctrl;
> + void __iomem *base = dev->base_addr;
> + struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
> + struct rkisp1_sensor_info *sensor = dev->active_sensor;
> + int lanes;
> +
> + /*
> + * sensor->mbus is set in isp or d-phy notifier_bound function
> + */
> + switch (sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) {
> + case V4L2_MBUS_CSI2_4_LANE:
> + lanes = 4;
> + break;
> + case V4L2_MBUS_CSI2_3_LANE:
> + lanes = 3;
> + break;
> + case V4L2_MBUS_CSI2_2_LANE:
> + lanes = 2;
> + break;
> + case V4L2_MBUS_CSI2_1_LANE:
> + lanes = 1;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
> + CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
> + CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
> + CIF_MIPI_CTRL_CLOCKLANE_ENA;
> +
> + writel(mipi_ctrl, base + CIF_MIPI_CTRL);
> +
> + /* Configure Data Type and Virtual Channel */
> + writel(CIF_MIPI_DATA_SEL_DT(in_fmt->mipi_dt) | CIF_MIPI_DATA_SEL_VC(0),
> + base + CIF_MIPI_IMG_DATA_SEL);
> +
> + /* Clear MIPI interrupts */
> + writel(~0, base + CIF_MIPI_ICR);
> + /*
> + * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for
> + * isp bus may be dead when switch isp.
> + */
> + writel(CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY |
> + CIF_MIPI_SYNC_FIFO_OVFLW(0x03) | CIF_MIPI_ADD_DATA_OVFLW,
> + base + CIF_MIPI_IMSC);
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "\n MIPI_CTRL 0x%08x\n"
> + " MIPI_IMG_DATA_SEL 0x%08x\n"
> + " MIPI_STATUS 0x%08x\n"
> + " MIPI_IMSC 0x%08x\n",
> + readl(base + CIF_MIPI_CTRL),
> + readl(base + CIF_MIPI_IMG_DATA_SEL),
> + readl(base + CIF_MIPI_STATUS),
> + readl(base + CIF_MIPI_IMSC));
> +
> + return 0;
> +}
> +
> +/* Configure MUX */
> +static int rkisp1_config_path(struct rkisp1_device *dev)
> +{
> + int ret = 0;
> + struct rkisp1_sensor_info *sensor = dev->active_sensor;
> + u32 dpcl = readl(dev->base_addr + CIF_VI_DPCL);
> +
> + if (sensor->mbus.type == V4L2_MBUS_BT656 ||
> + sensor->mbus.type == V4L2_MBUS_PARALLEL) {
> + ret = rkisp1_config_dvp(dev);
> + dpcl |= CIF_VI_DPCL_IF_SEL_PARALLEL;
> + } else if (sensor->mbus.type == V4L2_MBUS_CSI2) {
> + ret = rkisp1_config_mipi(dev);
> + dpcl |= CIF_VI_DPCL_IF_SEL_MIPI;
> + }
> +
> + writel(dpcl, dev->base_addr + CIF_VI_DPCL);
> +
> + return ret;
> +}
> +
> +/* Hareware configure Entry */
> +static int rkisp1_config_cif(struct rkisp1_device *dev)
> +{
> + int ret = 0;
> + u32 cif_id;
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "SP state = %d, MP state = %d\n",
> + dev->stream[RKISP1_STREAM_SP].state,
> + dev->stream[RKISP1_STREAM_MP].state);
> +
> + cif_id = readl(dev->base_addr + CIF_VI_ID);
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "CIF_ID 0x%08x\n", cif_id);
> +
> + ret = rkisp1_config_isp(dev);
> + if (ret < 0)
> + return ret;
> + ret = rkisp1_config_path(dev);
> + if (ret < 0)
> + return ret;
> + rkisp1_config_ism(dev);
> +
> + return 0;
> +}
> +
> +/* Mess register operations to stop isp */
> +static int rkisp1_isp_stop(struct rkisp1_device *dev)
> +{
> + void __iomem *base = dev->base_addr;
> + u32 val;
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "SP state = %d, MP state = %d\n",
> + dev->stream[RKISP1_STREAM_SP].state,
> + dev->stream[RKISP1_STREAM_MP].state);
> +
> + /*
> + * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
> + * Stop ISP(isp) ->wait for ISP isp off
> + */
> + /* stop and clear MI, MIPI, and ISP interrupts */
> + writel(0, base + CIF_MIPI_IMSC);
> + writel(~0, base + CIF_MIPI_ICR);
> +
> + writel(0, base + CIF_ISP_IMSC);
> + writel(~0, base + CIF_ISP_ICR);
> +
> + writel(0, base + CIF_MI_IMSC);
> + writel(~0, base + CIF_MI_ICR);
> + val = readl(base + CIF_MIPI_CTRL);
> + writel(val & (~CIF_MIPI_CTRL_OUTPUT_ENA), base + CIF_MIPI_CTRL);
> + /* stop ISP */
> + val = readl(base + CIF_ISP_CTRL);
> + val &= ~(CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE);
> + writel(val, base + CIF_ISP_CTRL);
> +
> + val = readl(base + CIF_ISP_CTRL);
> + writel(val | CIF_ISP_CTRL_ISP_CFG_UPD, base + CIF_ISP_CTRL);
> +
> + readx_poll_timeout(readl, base + CIF_ISP_RIS,
> + val, val & CIF_ISP_OFF, 20, 100);
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "state(MP:%d, SP:%d), MI_CTRL:%x, ISP_CTRL:%x, MIPI_CTRL:%x\n",
> + dev->stream[RKISP1_STREAM_SP].state,
> + dev->stream[RKISP1_STREAM_MP].state,
> + readl(base + CIF_MI_CTRL),
> + readl(base + CIF_ISP_CTRL),
> + readl(base + CIF_MIPI_CTRL));
> +
> + writel(CIF_IRCL_MIPI_SW_RST | CIF_IRCL_ISP_SW_RST, base + CIF_IRCL);
> + writel(0x0, base + CIF_IRCL);
> +
> + return 0;
> +}
> +
> +/* Mess register operations to start isp */
> +static int rkisp1_isp_start(struct rkisp1_device *dev)
> +{
> + struct rkisp1_sensor_info *sensor = dev->active_sensor;
> + void __iomem *base = dev->base_addr;
> + u32 val;
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "SP state = %d, MP state = %d\n",
> + dev->stream[RKISP1_STREAM_SP].state,
> + dev->stream[RKISP1_STREAM_MP].state);
> +
> + /* Activate MIPI */
> + if (sensor->mbus.type == V4L2_MBUS_CSI2) {
> + val = readl(base + CIF_MIPI_CTRL);
> + writel(val | CIF_MIPI_CTRL_OUTPUT_ENA, base + CIF_MIPI_CTRL);
> + }
> + /* Activate ISP */
> + val = readl(base + CIF_ISP_CTRL);
> + val |= CIF_ISP_CTRL_ISP_CFG_UPD | CIF_ISP_CTRL_ISP_ENABLE |
> + CIF_ISP_CTRL_ISP_INFORM_ENABLE;
> + writel(val, base + CIF_ISP_CTRL);
> +
> + /* XXX: Is the 1000us too long?
> + * CIF spec says to wait for sufficient time after enabling
> + * the MIPI interface and before starting the sensor output.
> + */
> + usleep_range(1000, 1200);
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "SP state = %d, MP state = %d MI_CTRL 0x%08x\n"
> + " ISP_CTRL 0x%08x MIPI_CTRL 0x%08x\n",
> + dev->stream[RKISP1_STREAM_SP].state,
> + dev->stream[RKISP1_STREAM_MP].state,
> + readl(base + CIF_MI_CTRL),
> + readl(base + CIF_ISP_CTRL),
> + readl(base + CIF_MIPI_CTRL));
> +
> + return 0;
> +}
> +
> +static void rkisp1_config_clk(struct rkisp1_device *dev)
> +{
> + u32 val = CIF_ICCL_ISP_CLK | CIF_ICCL_CP_CLK | CIF_ICCL_MRSZ_CLK |
> + CIF_ICCL_SRSZ_CLK | CIF_ICCL_JPEG_CLK | CIF_ICCL_MI_CLK |
> + CIF_ICCL_IE_CLK | CIF_ICCL_MIPI_CLK | CIF_ICCL_DCROP_CLK;
> +
> + writel(val, dev->base_addr + CIF_ICCL);
> +}
> +
> +/***************************** isp sub-devs *******************************/
> +
> +static const struct ispsd_in_fmt rkisp1_isp_input_formats[] = {
> + {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW10,
> + .bayer_pat = RAW_BGGR,
> + .bus_width = 10,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW10,
> + .bayer_pat = RAW_RGGB,
> + .bus_width = 10,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW10,
> + .bayer_pat = RAW_GBRG,
> + .bus_width = 10,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW10,
> + .bayer_pat = RAW_GRBG,
> + .bus_width = 10,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW12,
> + .bayer_pat = RAW_RGGB,
> + .bus_width = 12,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW12,
> + .bayer_pat = RAW_BGGR,
> + .bus_width = 12,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW12,
> + .bayer_pat = RAW_GBRG,
> + .bus_width = 12,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW12,
> + .bayer_pat = RAW_GRBG,
> + .bus_width = 12,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW8,
> + .bayer_pat = RAW_RGGB,
> + .bus_width = 8,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW8,
> + .bayer_pat = RAW_BGGR,
> + .bus_width = 8,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW8,
> + .bayer_pat = RAW_GBRG,
> + .bus_width = 8,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
> + .fmt_type = FMT_BAYER,
> + .mipi_dt = CIF_CSI2_DT_RAW8,
> + .bayer_pat = RAW_GRBG,
> + .bus_width = 8,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
> + .fmt_type = FMT_YUV,
> + .mipi_dt = CIF_CSI2_DT_YUV422_8b,
> + .yuv_seq = CIF_ISP_ACQ_PROP_YCBYCR,
> + .bus_width = 16,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
> + .fmt_type = FMT_YUV,
> + .mipi_dt = CIF_CSI2_DT_YUV422_8b,
> + .yuv_seq = CIF_ISP_ACQ_PROP_YCRYCB,
> + .bus_width = 16,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
> + .fmt_type = FMT_YUV,
> + .mipi_dt = CIF_CSI2_DT_YUV422_8b,
> + .yuv_seq = CIF_ISP_ACQ_PROP_CBYCRY,
> + .bus_width = 16,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
> + .fmt_type = FMT_YUV,
> + .mipi_dt = CIF_CSI2_DT_YUV422_8b,
> + .yuv_seq = CIF_ISP_ACQ_PROP_CRYCBY,
> + .bus_width = 16,
> + },
> +};
> +
> +static const struct ispsd_out_fmt rkisp1_isp_output_formats[] = {
> + {
> + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
> + .fmt_type = FMT_YUV,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
> + .fmt_type = FMT_BAYER,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
> + .fmt_type = FMT_BAYER,
> + },
> +};
> +
> +static const struct ispsd_in_fmt *find_in_fmt(u32 mbus_code)
> +{
> + const struct ispsd_in_fmt *fmt;
> + int i, array_size = ARRAY_SIZE(rkisp1_isp_input_formats);
> +
> + for (i = 0; i < array_size; i++) {
> + fmt = &rkisp1_isp_input_formats[i];
> + if (fmt->mbus_code == mbus_code)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct ispsd_out_fmt *find_out_fmt(u32 mbus_code)
> +{
> + const struct ispsd_out_fmt *fmt;
> + int i, array_size = ARRAY_SIZE(rkisp1_isp_output_formats);
> +
> + for (i = 0; i < array_size; i++) {
> + fmt = &rkisp1_isp_output_formats[i];
> + if (fmt->mbus_code == mbus_code)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static int rkisp1_isp_sd_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + int i = code->index;
> +
> + if (code->pad == RKISP1_ISP_PAD_SINK) {
> + if (i >= ARRAY_SIZE(rkisp1_isp_input_formats))
> + return -EINVAL;
> + code->code = rkisp1_isp_input_formats[i].mbus_code;
> + } else {
> + if (i >= ARRAY_SIZE(rkisp1_isp_output_formats))
> + return -EINVAL;
> + code->code = rkisp1_isp_output_formats[i].mbus_code;
> + }
> +
> + return 0;
> +}
> +
> +#define sd_to_isp_sd(_sd) container_of(_sd, struct rkisp1_isp_subdev, sd)
> +static int rkisp1_isp_sd_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_mbus_framefmt *mf = &fmt->format;
> + struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
> +
> + if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
> + (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
> + return -EINVAL;
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
> + fmt->format = *mf;
> + return 0;
> + }
> +
> + if (fmt->pad == RKISP1_ISP_PAD_SINK) {
> + *mf = isp_sd->in_frm;
> + } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
> + /* format of source pad */
> + *mf = isp_sd->in_frm;
> + /* window size of source pad */
> + mf->width = isp_sd->out_crop.width;
> + mf->height = isp_sd->out_crop.height;
> + mf->quantization = isp_sd->quantization;
> + }
> + mf->field = V4L2_FIELD_NONE;
> +
> + return 0;
> +}
> +
> +static void rkisp1_isp_sd_try_fmt(struct v4l2_subdev *sd,
> + unsigned int pad,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
> + const struct ispsd_in_fmt *in_fmt;
> + const struct ispsd_out_fmt *out_fmt;
> +
> + switch (pad) {
> + case RKISP1_ISP_PAD_SINK:
> + in_fmt = find_in_fmt(fmt->code);
> + if (in_fmt)
> + fmt->code = in_fmt->mbus_code;
> + else
> + fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
> + fmt->width = clamp_t(u32, fmt->width, CIF_ISP_INPUT_W_MIN,
> + CIF_ISP_INPUT_W_MAX);
> + fmt->height = clamp_t(u32, fmt->height, CIF_ISP_INPUT_H_MIN,
> + CIF_ISP_INPUT_H_MAX);
> + break;
> + case RKISP1_ISP_PAD_SOURCE_PATH:
> + out_fmt = find_out_fmt(fmt->code);
> + if (out_fmt)
> + fmt->code = out_fmt->mbus_code;
> + else
> + fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
> + /* window size is set in s_selection */
> + fmt->width = isp_sd->out_crop.width;
> + fmt->height = isp_sd->out_crop.height;
> + /* full range by default */
> + if (!fmt->quantization)
> + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> + break;
> + }
> +
> + fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int rkisp1_isp_sd_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
> + struct v4l2_mbus_framefmt *mf = &fmt->format;
> +
> + if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
> + (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
> + return -EINVAL;
> +
> + rkisp1_isp_sd_try_fmt(sd, fmt->pad, mf);
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> + struct v4l2_mbus_framefmt *try_mf;
> +
> + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
> + *try_mf = *mf;
> + return 0;
> + }
> +
> + if (fmt->pad == RKISP1_ISP_PAD_SINK) {
> + const struct ispsd_in_fmt *in_fmt;
> +
> + in_fmt = find_in_fmt(mf->code);
> + isp_sd->in_fmt = *in_fmt;
> + isp_sd->in_frm = *mf;
> + } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
> + const struct ispsd_out_fmt *out_fmt;
> +
> + /* Ignore width/height */
> + out_fmt = find_out_fmt(mf->code);
> + isp_sd->out_fmt = *out_fmt;
> + /*
> + * It is quantization for output,
> + * isp use bt601 limit-range in internal
> + */
> + isp_sd->quantization = mf->quantization;
> + }
> +
> + return 0;
> +}
> +
> +static void rkisp1_isp_sd_try_crop(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_selection *sel)
> +{
> + struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
> + struct v4l2_mbus_framefmt in_frm = isp_sd->in_frm;
> + struct v4l2_rect in_crop = isp_sd->in_crop;
> + struct v4l2_rect *input = &sel->r;
> +
> + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
> + in_frm = *v4l2_subdev_get_try_format(sd, cfg, RKISP1_ISP_PAD_SINK);
> + in_crop = *v4l2_subdev_get_try_crop(sd, cfg, RKISP1_ISP_PAD_SINK);
> + }
> +
> + input->left = ALIGN(input->left, 2);
> + input->width = ALIGN(input->width, 2);
> +
> + if (sel->pad == RKISP1_ISP_PAD_SINK) {
> + input->left = clamp_t(u32, input->left, 0, in_frm.width);
> + input->top = clamp_t(u32, input->top, 0, in_frm.height);
> + input->width = clamp_t(u32, input->width, CIF_ISP_INPUT_W_MIN,
> + in_frm.width - input->left);
> + input->height = clamp_t(u32, input->height,
> + CIF_ISP_INPUT_H_MIN,
> + in_frm.height - input->top);
> + } else if (sel->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
> + input->left = clamp_t(u32, input->left, 0, in_crop.width);
> + input->top = clamp_t(u32, input->top, 0, in_crop.height);
> + input->width = clamp_t(u32, input->width, CIF_ISP_OUTPUT_W_MIN,
> + in_crop.width - input->left);
> + input->height = clamp_t(u32, input->height, CIF_ISP_OUTPUT_H_MIN,
> + in_crop.height - input->top);
> + }
> +}
> +
> +static int rkisp1_isp_sd_get_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_selection *sel)
> +{
> + struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
> +
> + if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
> + sel->pad != RKISP1_ISP_PAD_SINK)
> + return -EINVAL;
> +
> + switch (sel->target) {
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + if (sel->pad == RKISP1_ISP_PAD_SINK) {
> + sel->r.height = isp_sd->in_frm.height;
> + sel->r.width = isp_sd->in_frm.width;
> + sel->r.left = 0;
> + sel->r.top = 0;
> + } else {
> + sel->r = isp_sd->in_crop;
> + }
> + break;
> + case V4L2_SEL_TGT_CROP:
> + if (sel->pad == RKISP1_ISP_PAD_SINK)
> + sel->r = isp_sd->in_crop;
> + else
> + sel->r = isp_sd->out_crop;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
This is valid for TGT_CROP, but I'm not sure about TGT_CROP_BOUNDS.
For PAD_SINK the crop bounds are that of v4l2_subdev_get_try_format(sd, cfg, PAD_SINK),
and for PAD_SOURCE_PATH is would be v4l2_subdev_get_try_crop(sd, cfg, PAD_SINK).
I think.
Right now with this code it would (when in TRY mode) return the 'try-crop' value
instead of a valid CROP_BOUNDS.
> + struct v4l2_rect *try_sel;
> +
> + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
> + sel->r = *try_sel;
> + }
> +
> + return 0;
> +}
> +
> +static int rkisp1_isp_sd_set_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_selection *sel)
> +{
> + struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
> + struct rkisp1_device *dev = sd_to_isp_dev(sd);
> +
> + if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
> + sel->pad != RKISP1_ISP_PAD_SINK)
> + return -EINVAL;
> + if (sel->target != V4L2_SEL_TGT_CROP)
> + return -EINVAL;
> +
> + v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
> + "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad,
> + sel->r.left, sel->r.top, sel->r.width, sel->r.height);
> + rkisp1_isp_sd_try_crop(sd, cfg, sel);
> +
> + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
> + struct v4l2_rect *try_sel;
> +
> + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
> + *try_sel = sel->r;
> + return 0;
> + }
> +
> + if (sel->pad == RKISP1_ISP_PAD_SINK)
> + isp_sd->in_crop = sel->r;
> + else
> + isp_sd->out_crop = sel->r;
> +
> + return 0;
> +}
> +
> +static int rkisp1_isp_sd_s_stream(struct v4l2_subdev *sd, int on)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + struct rkisp1_sensor_info *sensor;
> + struct v4l2_subdev *sensor_sd;
> + int ret = 0;
> +
> + if (!on)
> + return rkisp1_isp_stop(isp_dev);
> +
> + sensor_sd = get_remote_sensor(sd);
> + if (!sensor_sd)
> + return -ENODEV;
> +
> + sensor = sd_to_sensor(isp_dev, sensor_sd);
> + /*
> + * Update sensor bus configuration. This is only effective
> + * for sensors chained off an external CSI2 PHY.
> + */
> + ret = v4l2_subdev_call(sensor->sd, video, g_mbus_config,
> + &sensor->mbus);
> + if (ret && ret != -ENOIOCTLCMD)
> + return ret;
> + isp_dev->active_sensor = sensor;
> +
> + atomic_set(&isp_dev->isp_sdev.frm_sync_seq, 0);
> + ret = rkisp1_config_cif(isp_dev);
> + if (ret < 0)
> + return ret;
> +
> + return rkisp1_isp_start(isp_dev);
> +}
> +
> +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + int ret;
> +
> + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
> +
> + if (on) {
> + ret = pm_runtime_get_sync(isp_dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + rkisp1_config_clk(isp_dev);
> + } else {
> + ret = pm_runtime_put(isp_dev->dev);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rkisp1_subdev_link_validate(struct media_link *link)
> +{
> + if (link->source->index == RKISP1_ISP_PAD_SINK_PARAMS)
> + return 0;
> +
> + return v4l2_subdev_link_validate(link);
> +}
> +
> +static int rkisp1_subdev_fmt_link_validate(struct v4l2_subdev *sd,
> + struct media_link *link,
> + struct v4l2_subdev_format *source_fmt,
> + struct v4l2_subdev_format *sink_fmt)
> +{
> + if (source_fmt->format.code != sink_fmt->format.code)
> + return -EINVAL;
> +
> + /* Crop is available */
> + if (source_fmt->format.width < sink_fmt->format.width ||
> + source_fmt->format.height < sink_fmt->format.height)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static void
> +riksp1_isp_queue_event_sof(struct rkisp1_isp_subdev *isp)
> +{
> + struct v4l2_event event = {
> + .type = V4L2_EVENT_FRAME_SYNC,
> + .u.frame_sync.frame_sequence =
> + atomic_inc_return(&isp->frm_sync_seq) - 1,
> + };
> + v4l2_event_queue(isp->sd.devnode, &event);
> +}
> +
> +static int rkisp1_isp_sd_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + if (sub->type != V4L2_EVENT_FRAME_SYNC)
> + return -EINVAL;
> +
> + /* Line number. For now only zero accepted. */
> + if (sub->id != 0)
> + return -EINVAL;
> +
> + return v4l2_event_subscribe(fh, sub, 0, NULL);
> +}
> +
> +static const struct v4l2_subdev_pad_ops rkisp1_isp_sd_pad_ops = {
> + .enum_mbus_code = rkisp1_isp_sd_enum_mbus_code,
> + .get_selection = rkisp1_isp_sd_get_selection,
> + .set_selection = rkisp1_isp_sd_set_selection,
> + .get_fmt = rkisp1_isp_sd_get_fmt,
> + .set_fmt = rkisp1_isp_sd_set_fmt,
> + .link_validate = rkisp1_subdev_fmt_link_validate,
> +};
> +
> +static const struct media_entity_operations rkisp1_isp_sd_media_ops = {
> + .link_validate = rkisp1_subdev_link_validate,
> +};
> +
> +static const struct v4l2_subdev_video_ops rkisp1_isp_sd_video_ops = {
> + .s_stream = rkisp1_isp_sd_s_stream,
> +};
> +
> +static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
> + .subscribe_event = rkisp1_isp_sd_subs_evt,
> + .unsubscribe_event = v4l2_event_subdev_unsubscribe,
> + .s_power = rkisp1_isp_sd_s_power,
> +};
> +
> +static struct v4l2_subdev_ops rkisp1_isp_sd_ops = {
> + .core = &rkisp1_isp_core_ops,
> + .video = &rkisp1_isp_sd_video_ops,
> + .pad = &rkisp1_isp_sd_pad_ops,
> +};
> +
> +static void rkisp1_isp_sd_init_default_fmt(struct rkisp1_isp_subdev *isp_sd)
> +{
> + struct v4l2_mbus_framefmt *in_frm = &isp_sd->in_frm;
> + struct v4l2_rect *in_crop = &isp_sd->in_crop;
> + struct v4l2_rect *out_crop = &isp_sd->out_crop;
> + struct ispsd_in_fmt *in_fmt = &isp_sd->in_fmt;
> + struct ispsd_out_fmt *out_fmt = &isp_sd->out_fmt;
> +
> + *in_fmt = rkisp1_isp_input_formats[0];
> + in_frm->width = RKISP1_DEFAULT_WIDTH;
> + in_frm->height = RKISP1_DEFAULT_HEIGHT;
> + in_frm->code = in_fmt->mbus_code;
> +
> + in_crop->width = in_frm->width;
> + in_crop->height = in_frm->height;
> + in_crop->left = 0;
> + in_crop->top = 0;
> +
> + /* propagate to source */
> + *out_crop = *in_crop;
> + *out_fmt = rkisp1_isp_output_formats[0];
> +}
> +
> +int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
> + struct v4l2_device *v4l2_dev)
> +{
> + struct rkisp1_isp_subdev *isp_sdev = &isp_dev->isp_sdev;
> + struct v4l2_subdev *sd = &isp_sdev->sd;
> + int ret;
> +
> + v4l2_subdev_init(sd, &rkisp1_isp_sd_ops);
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> + sd->entity.ops = &rkisp1_isp_sd_media_ops;
> + snprintf(sd->name, sizeof(sd->name), "rkisp1-isp-subdev");
> +
> + isp_sdev->pads[RKISP1_ISP_PAD_SINK].flags =
> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> + isp_sdev->pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
> + isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_PATH].flags = MEDIA_PAD_FL_SOURCE;
> + isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
> + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> + ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX,
> + isp_sdev->pads);
> + if (ret < 0)
> + return ret;
> +
> + sd->owner = THIS_MODULE;
> + v4l2_set_subdevdata(sd, isp_dev);
> +
> + sd->grp_id = GRP_ID_ISP;
> + ret = v4l2_device_register_subdev(v4l2_dev, sd);
> + if (ret < 0) {
> + v4l2_err(sd, "Failed to register isp subdev\n");
> + goto err_cleanup_media_entity;
> + }
> +
> + rkisp1_isp_sd_init_default_fmt(isp_sdev);
> +
> + return 0;
> +err_cleanup_media_entity:
> + media_entity_cleanup(&sd->entity);
> + return ret;
> +}
> +
> +void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev)
> +{
> + struct v4l2_subdev *sd = &isp_dev->isp_sdev.sd;
> +
> + v4l2_device_unregister_subdev(sd);
> + media_entity_cleanup(&sd->entity);
> +}
> +
> +/**************** Interrupter Handler ****************/
> +
> +void rkisp1_mipi_isr(unsigned int mis, struct rkisp1_device *dev)
> +{
> + struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
> + void __iomem *base = dev->base_addr;
> + u32 val;
> +
> + writel(~0, base + CIF_MIPI_ICR);
> +
> + /*
> + * Disable DPHY errctrl interrupt, because this dphy
> + * erctrl signal is asserted until the next changes
> + * of line state. This time is may be too long and cpu
> + * is hold in this interrupt.
> + */
> + if (mis & CIF_MIPI_ERR_CTRL(0x0f)) {
> + val = readl(base + CIF_MIPI_IMSC);
> + writel(val & ~CIF_MIPI_ERR_CTRL(0x0f), base + CIF_MIPI_IMSC);
> + dev->isp_sdev.dphy_errctrl_disabled = true;
> + }
> +
> + /*
> + * Enable DPHY errctrl interrupt again, if mipi have receive
> + * the whole frame without any error.
> + */
> + if (mis == CIF_MIPI_FRAME_END) {
> + /*
> + * Enable DPHY errctrl interrupt again, if mipi have receive
> + * the whole frame without any error.
> + */
> + if (dev->isp_sdev.dphy_errctrl_disabled) {
> + val = readl(base + CIF_MIPI_IMSC);
> + val |= CIF_MIPI_ERR_CTRL(0x0f);
> + writel(val, base + CIF_MIPI_IMSC);
> + dev->isp_sdev.dphy_errctrl_disabled = false;
> + }
> + } else {
> + v4l2_warn(v4l2_dev, "MIPI mis error: 0x%08x\n", mis);
> + }
> +}
> +
> +void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev)
> +{
> + void __iomem *base = dev->base_addr;
> + unsigned int isp_mis_tmp = 0;
> + unsigned int isp_err = 0;
> +
> + /* start edge of v_sync */
> + if (isp_mis & CIF_ISP_V_START) {
> + riksp1_isp_queue_event_sof(&dev->isp_sdev);
> +
> + writel(CIF_ISP_V_START, base + CIF_ISP_ICR);
> + isp_mis_tmp = readl(base + CIF_ISP_MIS);
> + if (isp_mis_tmp & CIF_ISP_V_START)
> + v4l2_err(&dev->v4l2_dev, "isp icr v_statr err: 0x%x\n",
> + isp_mis_tmp);
> + }
> +
> + if ((isp_mis & CIF_ISP_PIC_SIZE_ERROR)) {
> + /* Clear pic_size_error */
> + writel(CIF_ISP_PIC_SIZE_ERROR, base + CIF_ISP_ICR);
> + isp_err = readl(base + CIF_ISP_ERR);
> + v4l2_err(&dev->v4l2_dev,
> + "CIF_ISP_PIC_SIZE_ERROR (0x%08x)", isp_err);
> + writel(isp_err, base + CIF_ISP_ERR_CLR);
> + } else if ((isp_mis & CIF_ISP_DATA_LOSS)) {
> + /* Clear data_loss */
> + writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
> + v4l2_err(&dev->v4l2_dev, "CIF_ISP_DATA_LOSS\n");
> + writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
> + }
> +
> + /* sampled input frame is complete */
> + if (isp_mis & CIF_ISP_FRAME_IN) {
> + writel(CIF_ISP_FRAME_IN, base + CIF_ISP_ICR);
> + isp_mis_tmp = readl(base + CIF_ISP_MIS);
> + if (isp_mis_tmp & CIF_ISP_FRAME_IN)
> + v4l2_err(&dev->v4l2_dev, "isp icr frame_in err: 0x%x\n",
> + isp_mis_tmp);
> + }
> +
> + /* frame was completely put out */
> + if (isp_mis & CIF_ISP_FRAME) {
> + u32 isp_ris = 0;
> + /* Clear Frame In (ISP) */
> + writel(CIF_ISP_FRAME, base + CIF_ISP_ICR);
> + isp_mis_tmp = readl(base + CIF_ISP_MIS);
> + if (isp_mis_tmp & CIF_ISP_FRAME)
> + v4l2_err(&dev->v4l2_dev,
> + "isp icr frame end err: 0x%x\n", isp_mis_tmp);
> +
> + isp_ris = readl(base + CIF_ISP_RIS);
> + if (isp_ris & (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN |
> + CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY))
> + rkisp1_stats_isr(&dev->stats_vdev, isp_ris);
> + }
> +
> + /*
> + * Then update changed configs. Some of them involve
> + * lot of register writes. Do those only one per frame.
> + * Do the updates in the order of the processing flow.
> + */
> + rkisp1_params_isr(&dev->params_vdev, isp_mis);
> +}
> diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.h b/drivers/media/platform/rockchip/isp1/rkisp1.h
> new file mode 100644
> index 000000000000..f0bbcbf0c827
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/rkisp1.h
> @@ -0,0 +1,105 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef _RKISP1_H
> +#define _RKISP1_H
> +
> +#include <linux/platform_device.h>
> +#include <media/v4l2-fwnode.h>
> +#include "common.h"
> +
> +struct rkisp1_stream;
> +
> +/*
> + * struct ispsd_in_fmt - ISP intput-pad format
> + *
> + * Translate mbus_code to hardware format values
> + *
> + * @bus_width: used for parallel
> + */
> +struct ispsd_in_fmt {
> + u32 mbus_code;
> + u8 fmt_type;
> + u32 mipi_dt;
> + u32 yuv_seq;
> + enum rkisp1_fmt_raw_pat_type bayer_pat;
> + u8 bus_width;
> +};
> +
> +struct ispsd_out_fmt {
> + u32 mbus_code;
> + u8 fmt_type;
> +};
> +
> +struct rkisp1_ie_config {
> + unsigned int effect;
> +};
> +
> +enum rkisp1_isp_pad {
> + RKISP1_ISP_PAD_SINK,
> + RKISP1_ISP_PAD_SINK_PARAMS,
> + RKISP1_ISP_PAD_SOURCE_PATH,
> + RKISP1_ISP_PAD_SOURCE_STATS,
> + RKISP1_ISP_PAD_MAX
> +};
> +
> +/*
> + * struct rkisp1_isp_subdev - ISP sub-device
> + *
> + * See Cropping regions of ISP in rkisp1.c for details
> + * @in_frm: input size, don't have to be equal to sensor size
> + * @in_fmt: intput format
> + * @in_crop: crop for sink pad
> + * @out_fmt: output format
> + * @out_crop: output size
> + *
> + * @dphy_errctrl_disabled: if dphy errctrl is disabled(avoid endless interrupt)
> + * @frm_sync_seq: frame sequence, to sync frame_id between video devices.
> + * @quantization: output quantization
> + */
> +struct rkisp1_isp_subdev {
> + struct v4l2_subdev sd;
> + struct media_pad pads[RKISP1_ISP_PAD_MAX];
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct v4l2_mbus_framefmt in_frm;
> + struct ispsd_in_fmt in_fmt;
> + struct v4l2_rect in_crop;
> + struct ispsd_out_fmt out_fmt;
> + struct v4l2_rect out_crop;
> + bool dphy_errctrl_disabled;
> + atomic_t frm_sync_seq;
> + enum v4l2_quantization quantization;
> +};
> +
> +int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
> + struct v4l2_device *v4l2_dev);
> +
> +void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev);
> +
> +void rkisp1_mipi_isr(unsigned int mipi_mis, struct rkisp1_device *dev);
> +
> +void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev);
> +
> +static inline
> +struct ispsd_out_fmt *rkisp1_get_ispsd_out_fmt(struct rkisp1_isp_subdev *isp_sdev)
> +{
> + return &isp_sdev->out_fmt;
> +}
> +
> +static inline
> +struct ispsd_in_fmt *rkisp1_get_ispsd_in_fmt(struct rkisp1_isp_subdev *isp_sdev)
> +{
> + return &isp_sdev->in_fmt;
> +}
> +
> +static inline
> +struct v4l2_rect *rkisp1_get_isp_sd_win(struct rkisp1_isp_subdev *isp_sdev)
> +{
> + return &isp_sdev->out_crop;
> +}
> +
> +#endif /* _RKISP1_H */
>
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver
2018-03-08 9:47 ` [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver Jacob Chen
@ 2018-03-09 13:59 ` Hans Verkuil
0 siblings, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 13:59 UTC (permalink / raw)
To: linux-arm-kernel
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> Add the capture video driver for rockchip isp1 statistics block.
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
> Signed-off-by: Jacob Chen <cc@rock-chips.com>
> Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 07/17] media: rkisp1: add ISP1 params driver
2018-03-08 9:47 ` [PATCH v6 07/17] media: rkisp1: add ISP1 params driver Jacob Chen
@ 2018-03-09 14:03 ` Hans Verkuil
0 siblings, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 14:03 UTC (permalink / raw)
To: linux-arm-kernel
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> Add the output video driver that accept params from userspace.
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
> Signed-off-by: Jacob Chen <cc@rock-chips.com>
> Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
> 2 files changed, 1588 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/isp_params.c b/drivers/media/platform/rockchip/isp1/isp_params.c
> new file mode 100644
> index 000000000000..747108e02836
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/isp_params.c
<snip>
> +static int rkisp1_params_querycap(struct file *file,
> + void *priv, struct v4l2_capability *cap)
> +{
> + struct video_device *vdev = video_devdata(file);
> +
> + strcpy(cap->driver, DRIVER_NAME);
Use strlcpy!
> + strlcpy(cap->card, vdev->name, sizeof(cap->card));
> + strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info));
> +
> + return 0;
> +}
> +
> +/* ISP params video device IOCTLs */
> +static const struct v4l2_ioctl_ops rkisp1_params_ioctl = {
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> + .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
> + .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> + .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> + .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
> + .vidioc_querycap = rkisp1_params_querycap,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
> +
> + *num_buffers = clamp_t(u32, *num_buffers,
> + RKISP1_ISP_PARAMS_REQ_BUFS_MIN,
> + RKISP1_ISP_PARAMS_REQ_BUFS_MAX);
> +
> + *num_planes = 1;
> +
> + sizes[0] = sizeof(struct rkisp1_isp_params_cfg);
> +
> + INIT_LIST_HEAD(¶ms_vdev->params);
> + params_vdev->first_params = true;
> +
> + return 0;
> +}
> +
> +static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct rkisp1_buffer *params_buf = to_rkisp1_buffer(vbuf);
> + struct vb2_queue *vq = vb->vb2_queue;
> + struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
> + struct rkisp1_isp_params_cfg *new_params;
> + unsigned long flags;
> +
> + unsigned int cur_frame_id = -1;
> + cur_frame_id = atomic_read(¶ms_vdev->dev->isp_sdev.frm_sync_seq) - 1;
> +
> + if (params_vdev->first_params) {
> + new_params = (struct rkisp1_isp_params_cfg *)
> + (vb2_plane_vaddr(vb, 0));
> + vbuf->sequence = cur_frame_id;
> + vb2_buffer_done(¶ms_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
> + params_vdev->first_params = false;
> + params_vdev->cur_params = *new_params;
> + return;
> + }
> +
> + params_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
> + spin_lock_irqsave(¶ms_vdev->config_lock, flags);
> + list_add_tail(¶ms_buf->queue, ¶ms_vdev->params);
> + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
> +}
> +
> +static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> + if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_isp_params_cfg))
> + return -EINVAL;
> +
> + vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_isp_params_cfg));
> +
> + return 0;
> +}
> +
> +static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> + struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
> + struct rkisp1_buffer *buf;
> + unsigned long flags;
> + int i;
> +
> + /* stop params input firstly */
> + spin_lock_irqsave(¶ms_vdev->config_lock, flags);
> + params_vdev->streamon = false;
> + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
> +
> + for (i = 0; i < RKISP1_ISP_PARAMS_REQ_BUFS_MAX; i++) {
> + spin_lock_irqsave(¶ms_vdev->config_lock, flags);
> + if (!list_empty(¶ms_vdev->params)) {
> + buf = list_first_entry(¶ms_vdev->params,
> + struct rkisp1_buffer, queue);
> + list_del(&buf->queue);
> + spin_unlock_irqrestore(¶ms_vdev->config_lock,
> + flags);
> + } else {
> + spin_unlock_irqrestore(¶ms_vdev->config_lock,
> + flags);
> + break;
> + }
> +
> + if (buf)
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + buf = NULL;
> + }
> +}
> +
> +static int
> +rkisp1_params_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
> +{
> + struct rkisp1_isp_params_vdev *params_vdev = queue->drv_priv;
> + unsigned long flags;
> +
> + spin_lock_irqsave(¶ms_vdev->config_lock, flags);
> + params_vdev->streamon = true;
> + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
> +
> + return 0;
> +}
> +
> +static struct vb2_ops rkisp1_params_vb2_ops = {
> + .queue_setup = rkisp1_params_vb2_queue_setup,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .buf_queue = rkisp1_params_vb2_buf_queue,
> + .buf_prepare = rkisp1_params_vb2_buf_prepare,
> + .start_streaming = rkisp1_params_vb2_start_streaming,
> + .stop_streaming = rkisp1_params_vb2_stop_streaming,
> +
> +};
> +
> +struct v4l2_file_operations rkisp1_params_fops = {
> + .mmap = vb2_fop_mmap,
> + .unlocked_ioctl = video_ioctl2,
> + .poll = vb2_fop_poll,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release
> +};
> +
> +static int
> +rkisp1_params_init_vb2_queue(struct vb2_queue *q,
> + struct rkisp1_isp_params_vdev *params_vdev)
> +{
> + struct rkisp1_vdev_node *node;
> +
> + node = queue_to_node(q);
> +
> + q->type = V4L2_BUF_TYPE_META_OUTPUT;
> + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
> + q->drv_priv = params_vdev;
> + q->ops = &rkisp1_params_vb2_ops;
> + q->mem_ops = &vb2_vmalloc_memops;
> + q->buf_struct_size = sizeof(struct rkisp1_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &node->vlock;
> +
> + return vb2_queue_init(q);
> +}
> +
> +static void rkisp1_init_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
> +{
> + params_vdev->vdev_fmt.fmt.meta.dataformat =
> + V4L2_META_FMT_RK_ISP1_PARAMS;
> + params_vdev->vdev_fmt.fmt.meta.buffersize =
> + sizeof(struct rkisp1_isp_params_cfg);
> +}
> +
> +int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
> + struct v4l2_device *v4l2_dev,
> + struct rkisp1_device *dev)
> +{
> + int ret;
> + struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
> + struct video_device *vdev = &node->vdev;
> +
> + params_vdev->dev = dev;
> + mutex_init(&node->vlock);
> + spin_lock_init(¶ms_vdev->config_lock);
> +
> + strlcpy(vdev->name, "rkisp1-input-params", sizeof(vdev->name));
> +
> + video_set_drvdata(vdev, params_vdev);
> + vdev->ioctl_ops = &rkisp1_params_ioctl;
> + vdev->fops = &rkisp1_params_fops;
> + vdev->release = video_device_release_empty;
> + /*
> + * Provide a mutex to v4l2 core. It will be used
> + * to protect all fops and v4l2 ioctls.
> + */
> + vdev->lock = &node->vlock;
> + vdev->v4l2_dev = v4l2_dev;
> + vdev->queue = &node->buf_queue;
> + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
As mentioned in my previous review: the patches adding Meta Output support
haven't been merged yet. This driver is likely to be the first to need this.
Please add Sakari's patches adding support for META_OUTPUT to this patch series
since it is likely that they will be merged together with this driver.
> + vdev->vfl_dir = VFL_DIR_TX;
> + rkisp1_params_init_vb2_queue(vdev->queue, params_vdev);
> + rkisp1_init_params_vdev(params_vdev);
> + video_set_drvdata(vdev, params_vdev);
> +
> + node->pad.flags = MEDIA_PAD_FL_SOURCE;
> + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
> + if (ret < 0)
> + goto err_release_queue;
> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> + if (ret < 0) {
> + dev_err(&vdev->dev,
> + "could not register Video for Linux device\n");
> + goto err_cleanup_media_entity;
> + }
> + return 0;
> +err_cleanup_media_entity:
> + media_entity_cleanup(&vdev->entity);
> +err_release_queue:
> + vb2_queue_release(vdev->queue);
> + return ret;
> +}
> +
> +void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
> +{
> + struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
> + struct video_device *vdev = &node->vdev;
> +
> + video_unregister_device(vdev);
> + media_entity_cleanup(&vdev->entity);
> + vb2_queue_release(vdev->queue);
> +}
> diff --git a/drivers/media/platform/rockchip/isp1/isp_params.h b/drivers/media/platform/rockchip/isp1/isp_params.h
> new file mode 100644
> index 000000000000..24602e11014d
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/isp_params.h
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef _RKISP1_ISP_H
> +#define _RKISP1_ISP_H
> +
> +#include <linux/rkisp1-config.h>
> +#include "common.h"
> +
> +/*
> + * struct rkisp1_isp_subdev - ISP input parameters device
> + *
> + * @cur_params: Current ISP parameters
> + * @first_params: the first params should take effect immediately
> + */
> +struct rkisp1_isp_params_vdev {
> + struct rkisp1_vdev_node vnode;
> + struct rkisp1_device *dev;
> +
> + spinlock_t config_lock;
> + struct list_head params;
> + struct rkisp1_isp_params_cfg cur_params;
> + struct v4l2_format vdev_fmt;
> + bool streamon;
> + bool first_params;
> +
> + enum v4l2_quantization quantization;
> + enum rkisp1_fmt_raw_pat_type raw_type;
> +};
> +
> +/* config params before ISP streaming */
> +void rkisp1_params_configure_isp(struct rkisp1_isp_params_vdev *params_vdev,
> + struct ispsd_in_fmt *in_fmt,
> + enum v4l2_quantization quantization);
> +void rkisp1_params_disable_isp(struct rkisp1_isp_params_vdev *params_vdev);
> +
> +int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
> + struct v4l2_device *v4l2_dev,
> + struct rkisp1_device *dev);
> +
> +void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev);
> +
> +void rkisp1_params_isr(struct rkisp1_isp_params_vdev *params_vdev, u32 isp_mis);
> +
> +#endif /* _RKISP1_ISP_H */
>
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 08/17] media: rkisp1: add capture device driver
[not found] ` <20180308094807.9443-9-jacob-chen@iotwrt.com>
@ 2018-03-09 14:07 ` Hans Verkuil
2018-04-17 8:44 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 14:07 UTC (permalink / raw)
To: linux-arm-kernel
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This is the capture device interface driver that provides the v4l2
> user interface. Frames can be received from ISP1.
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
> Signed-off-by: Jacob Chen <cc@rock-chips.com>
> Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Regards,
Hans
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (18 preceding siblings ...)
[not found] ` <20180308094807.9443-9-jacob-chen@iotwrt.com>
@ 2018-03-09 14:12 ` Hans Verkuil
2018-09-03 13:07 ` Hans Verkuil
20 siblings, 0 replies; 50+ messages in thread
From: Hans Verkuil @ 2018-03-09 14:12 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
This is getting very close. Besides this round of review comments the main
blocker is mbus_config. But Thomasz is looking at that, so I will wait until
that is resolved.
Also for your v7 include the patches from Sakari that add Meta Output support.
This patch series relies on those, so it is easiest to review if they are part
of the patch series.
Regards,
Hans
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> changes in V6:
> - add mipi txrx phy support
> - remove bool and enum from uapi header
> - add buf_prepare op
> - correct some spelling problems
> - return all queued buffers when starting stream failed
>
> changes in V5: Sync with local changes,
> - fix the SP height limit
> - speed up the second stream capture
> - the second stream can't force sync for rsz when start/stop streaming
> - add frame id to param vb2 buf
> - enable luminance maximum threshold
>
> changes in V4:
> - fix some bugs during development
> - move quantization settings to rkisp1 subdev
> - correct some spelling problems
> - describe ports in dt-binding documents
>
> changes in V3:
> - add some comments
> - fix wrong use of v4l2_async_subdev_notifier_register
> - optimize two paths capture at a time
> - remove compose
> - re-struct headers
> - add a tmp wiki page: http://opensource.rock-chips.com/wiki_Rockchip-isp1
>
> changes in V2:
> mipi-phy:
> - use async probing
> - make it be a child device of the GRF
> isp:
> - add dummy buffer
> - change the way to get bus configuration, which make it possible to
> add parallel sensor support in the future(without mipi-phy driver).
>
> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399 SoC.
>
> Wiki Pages:
> http://opensource.rock-chips.com/wiki_Rockchip-isp1
>
> The deprecated g_mbus_config op is not dropped in V6 because i am waiting tomasz's patches.
>
> v4l2-compliance for V6(isp params/stats nodes are passed):
>
> v4l2-compliance SHA : 93dc5f20727fede5097d67f8b9adabe4b8046d5b
>
> Compliance test for device /dev/video0:
>
> Driver Info:
> Driver name : rkisp1
> Card type : rkisp1
> Bus info : platform:ff910000.isp
> Driver version : 4.16.0
> Capabilities : 0x84201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
> Device Capabilities
> Device Caps : 0x04201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
> Media Driver Info:
> Driver name : rkisp1
> Model : rkisp1
> Serial :
> Bus info :
> Media version : 4.16.0
> Hardware revision: 0x00000000 (0)
> Driver version : 4.16.0
> Interface Info:
> ID : 0x03000007
> Type : V4L Video
> Entity Info:
> ID : 0x00000006 (6)
> Name : rkisp1_selfpath
> Function : V4L2 I/O
> Pad 0x01000009 : Sink
> Link 0x02000021: from remote pad 0x1000004 of entity 'rkisp1-isp-subdev': Data, Enabled
>
> Required ioctls:
> test MC information (see 'Media Driver Info' above): OK
> test VIDIOC_QUERYCAP: OK
>
> Allow for multiple opens:
> test second /dev/video0 open: OK
> test VIDIOC_QUERYCAP: OK
> test VIDIOC_G/S_PRIORITY: OK
> test for unlimited opens: OK
>
> Debug ioctls:
> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> test VIDIOC_LOG_STATUS: OK (Not Supported)
>
> Input ioctls:
> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> test VIDIOC_ENUMAUDIO: OK (Not Supported)
> test VIDIOC_G/S/ENUMINPUT: OK
> test VIDIOC_G/S_AUDIO: OK (Not Supported)
> Inputs: 1 Audio Inputs: 0 Tuners: 0
>
> Output ioctls:
> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> Outputs: 0 Audio Outputs: 0 Modulators: 0
>
> Input/Output configuration ioctls:
> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> test VIDIOC_G/S_EDID: OK (Not Supported)
>
> Control ioctls (Input 0):
> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> test VIDIOC_QUERYCTRL: OK
> test VIDIOC_G/S_CTRL: OK
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> Standard Controls: 9 Private Controls: 0
>
> Format ioctls (Input 0):
> test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> test VIDIOC_G/S_PARM: OK (Not Supported)
> test VIDIOC_G_FBUF: OK (Not Supported)
> fail: v4l2-test-formats.cpp(330): !colorspace
> fail: v4l2-test-formats.cpp(454): testColorspace(pix_mp.pixelformat, pix_mp.colorspace, pix_mp.ycbcr_enc, pix_m
> p.quantization)
> test VIDIOC_G_FMT: FAIL
> test VIDIOC_TRY_FMT: OK (Not Supported)
> test VIDIOC_S_FMT: OK (Not Supported)
> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> fail: v4l2-test-formats.cpp(1288): doioctl(node, VIDIOC_G_SELECTION, &sel) != EINVAL
> test Cropping: FAIL
> test Composing: OK (Not Supported)
> test Scaling: OK
>
> Codec ioctls (Input 0):
> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>
> Buffer ioctls (Input 0):
> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> fail: v4l2-test-buffers.cpp(525): VIDIOC_EXPBUF is supported, but the V4L2_MEMORY_MMAP support is missing, prob
> ably due to earlier failing format tests.
> test VIDIOC_EXPBUF: OK (Not Supported)
>
> Total: 44, Succeeded: 42, Failed: 2, Warnings: 0
>
> Jacob Chen (12):
> media: doc: add document for rkisp1 meta buffer format
> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
> media: rkisp1: add Rockchip ISP1 subdev driver
> media: rkisp1: add ISP1 statistics driver
> media: rkisp1: add ISP1 params driver
> media: rkisp1: add capture device driver
> media: rkisp1: add rockchip isp1 core driver
> dt-bindings: Document the Rockchip ISP1 bindings
> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
> ARM: dts: rockchip: add isp node for rk3288
> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
> MAINTAINERS: add entry for Rockchip ISP1 driver
>
> Jeffy Chen (1):
> media: rkisp1: Add user space ABI definitions
>
> Shunqian Zheng (3):
> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
> arm64: dts: rockchip: add isp0 node for rk3399
> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
>
> Wen Nuan (1):
> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
>
> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
> MAINTAINERS | 10 +
> arch/arm/boot/dts/rk3288.dtsi | 33 +
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
> drivers/media/platform/Kconfig | 10 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/rockchip/isp1/Makefile | 8 +
> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
> drivers/media/platform/rockchip/isp1/common.h | 110 ++
> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
> include/uapi/linux/rkisp1-config.h | 798 +++++++++
> include/uapi/linux/videodev2.h | 4 +
> 29 files changed, 9945 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
> create mode 100644 include/uapi/linux/rkisp1-config.h
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver
2018-03-08 9:47 ` [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver Jacob Chen
@ 2018-03-11 6:58 ` Baruch Siach
2018-05-16 8:08 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Baruch Siach @ 2018-03-11 6:58 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Thu, Mar 08, 2018 at 05:47:59PM +0800, Jacob Chen wrote:
> +config VIDEO_ROCKCHIP_ISP1
> + tristate "Rockchip Image Signal Processing v1 Unit driver"
> + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> + depends on ARCH_ROCKCHIP || COMPILE_TEST
> + select VIDEOBUF2_DMA_CONTIG
> + select V4L2_FWNODE
> + default n
> + ---help---
> + Support for ISP1 on the rockchip SoC.
I added 'select VIDEOBUF2_VMALLOC' here to fix link failure:
drivers/media/platform/rockchip/isp1/isp_stats.o: In function `rkisp1_register_stats_vdev':
isp_stats.c:(.text+0x80c): undefined reference to `vb2_vmalloc_memops'
isp_stats.c:(.text+0x814): undefined reference to `vb2_vmalloc_memops'
drivers/media/platform/rockchip/isp1/isp_params.o: In function `rkisp1_register_params_vdev':
isp_params.c:(.text+0x29b4): undefined reference to `vb2_vmalloc_memops'
isp_params.c:(.text+0x29bc): undefined reference to `vb2_vmalloc_memops'
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 08/17] media: rkisp1: add capture device driver
[not found] ` <20180308094807.9443-9-jacob-chen@iotwrt.com>
2018-03-09 14:07 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Hans Verkuil
@ 2018-04-17 8:44 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-04-17 8:44 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Thu, Mar 8, 2018 at 6:49 PM Jacob Chen <jacob-chen@iotwrt.com> wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
> This is the capture device interface driver that provides the v4l2
> user interface. Frames can be received from ISP1.
Thanks for the patch. Please find my comment inline.
[snip]
> +static int
> +rkisp1_start_streaming(struct vb2_queue *queue, unsigned int count)
> +{
> + struct rkisp1_stream *stream = queue->drv_priv;
> + struct rkisp1_vdev_node *node = &stream->vnode;
> + struct rkisp1_device *dev = stream->ispdev;
> + struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
> + int ret;
> +
> + if (WARN_ON(stream->state != RKISP1_STATE_READY))
> + goto return_queued_buf;
We jump out with ret unitialized here. For reference, it triggers a
compiler warning for me.
Note that rather than initializing ret at its definition, I'd recommend
adding an assignment before the goto statement. This will still let the
compiler issue warnings, without assuming that the default value is correct.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions
2018-03-08 9:47 ` [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions Jacob Chen
2018-03-09 13:34 ` Hans Verkuil
@ 2018-04-24 4:28 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-04-24 4:28 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Mar 8, 2018 at 6:48 PM Jacob Chen <jacob-chen@iotwrt.com> wrote:
[snip]
> +/**
> + * struct cifisp_lsc_config - Configuration used by Lens shading
correction
> + *
> + * refer to REF_01 for details
> + */
> +struct cifisp_lsc_config {
> + __u32 r_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 gr_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 gb_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> + __u32 b_data_tbl[CIFISP_LSC_DATA_TBL_SIZE];
> +
> + __u32 x_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
> + __u32 y_grad_tbl[CIFISP_LSC_GRAD_TBL_SIZE];
> +
> + __u32 x_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
> + __u32 y_size_tbl[CIFISP_LSC_SIZE_TBL_SIZE];
Looking at the code, we only need 12 bits of each, so perhaps it could make
sense to make those __u16? Also, the natural layout for these seems to be
two-dimensional, i.e. [CIFISP_LSC_NUM_SECTORS][CIFISP_LSC_NUM_SECTORS]. I
think it wouldn't be a problem to define it this way for UAPI too.
> + __u16 config_width;
> + __u16 config_height;
These 2 seem unused. Just making sure. If they are part of hardware LSC
configuration, then we should keep them.
[snip]
> +struct cifisp_awb_meas_config {
> + /*
> + * Note: currently the h and v offsets are mapped to grid offsets
> + */
Perhaps should be part of the kerneldoc comment above? Also, I don't seem
to understand what this means.
> + struct cifisp_window awb_wnd;
> + __u32 awb_mode;
> + __u8 max_y;
> + __u8 min_y;
> + __u8 max_csum;
> + __u8 min_c;
> + __u8 frames;
> + __u8 awb_ref_cr;
> + __u8 awb_ref_cb;
> + __u8 enable_ymax_cmp;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_awb_gain_config - Configuration used by auto white
balance gain
> + *
> + * out_data_x = ( AWB_GEAIN_X * in_data + 128) >> 8
typo: AWB_GAIN?
> + */
> +struct cifisp_awb_gain_config {
> + __u16 gain_red;
> + __u16 gain_green_r;
> + __u16 gain_blue;
> + __u16 gain_green_b;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_flt_config - Configuration used by ISP filtering
> + *
> + * @mode: ISP_FILT_MODE register fields (from enum cifisp_flt_mode)
> + * @grn_stage1: ISP_FILT_MODE register fields
> + * @chr_h_mode: ISP_FILT_MODE register fields
> + * @chr_v_mode: ISP_FILT_MODE register fields
Missing documentation for remaining fields.
> + *
> + * refer to REF_01 for details.
> + */
> +
> +struct cifisp_flt_config {
> + __u32 mode;
> + __u8 grn_stage1;
> + __u8 chr_h_mode;
> + __u8 chr_v_mode;
Maybe we could move u8 below u32 to optimize the alignment?
[snip]
> +/**
> + * struct cifisp_hst_config - Configuration used by Histogram
> + *
> + * @mode: histogram mode (from enum cifisp_histogram_mode)
> + * @histogram_predivider: process every stepsize pixel, all other pixels
are skipped
> + * @meas_window: coordinates of the measure window
> + * @hist_weight: weighting factor for sub-windows
> + */
> +struct cifisp_hst_config {
> + __u32 mode;
> + __u8 histogram_predivider;
> + struct cifisp_window meas_window;
Perhaps could swap the two above for better alignment?
> + __u8 hist_weight[CIFISP_HISTOGRAM_WEIGHT_GRIDS_SIZE];
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_aec_config - Configuration used by Auto Exposure Control
> + *
> + * @mode: Exposure measure mode (from enum cifisp_exp_meas_mode)
> + * @autostop: stop mode (from enum cifisp_exp_ctrl_autostop)
> + * @meas_window: coordinates of the measure window
> + */
> +struct cifisp_aec_config {
> + __u32 mode;
> + __u32 autostop;
> + struct cifisp_window meas_window;
> +} __attribute__ ((packed));
> +
> +/**
> + * struct cifisp_afc_config - Configuration used by Auto Focus Control
> + *
> + * @num_afm_win: max CIFISP_AFM_MAX_WINDOWS
> + * @afm_win: coordinates of the meas window
> + * @thres: threshold used for minimizing the influence of noise
> + * @var_shift: the number of bits for the shift operation at the end of
the calculation chain.
> + */
> +struct cifisp_afc_config {
> + __u8 num_afm_win;
> + struct cifisp_window afm_win[CIFISP_AFM_MAX_WINDOWS];
> + __u32 thres;
> + __u32 var_shift;
Perhaps could put afm_win[] and then num_afm_win here, for better alignment?
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
2018-03-09 13:57 ` Hans Verkuil
@ 2018-05-03 9:09 ` Baruch Siach
2018-05-07 6:13 ` Tomasz Figa
1 sibling, 1 reply; 50+ messages in thread
From: Baruch Siach @ 2018-05-03 9:09 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + int ret;
> +
> + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
> +
> + if (on) {
> + ret = pm_runtime_get_sync(isp_dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + rkisp1_config_clk(isp_dev);
> + } else {
> + ret = pm_runtime_put(isp_dev->dev);
I commented this line out to make more than one STREAMON work. Otherwise, the
second STREAMON hangs. I guess the bug is not this driver. Probably something
in drivers/soc/rockchip/pm_domains.c. Just noting that in case you or someone
on Cc would like to investigate it further.
I tested v4.16-rc4 on the Tinkerboard.
baruch
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-03 9:09 ` Baruch Siach
@ 2018-05-07 6:13 ` Tomasz Figa
2018-05-07 6:38 ` Baruch Siach
0 siblings, 1 reply; 50+ messages in thread
From: Tomasz Figa @ 2018-05-07 6:13 UTC (permalink / raw)
To: linux-arm-kernel
Hi Baruch,
On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Jacob,
> On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > +{
> > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > + int ret;
> > +
> > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
on);
> > +
> > + if (on) {
> > + ret = pm_runtime_get_sync(isp_dev->dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + rkisp1_config_clk(isp_dev);
> > + } else {
> > + ret = pm_runtime_put(isp_dev->dev);
> I commented this line out to make more than one STREAMON work. Otherwise,
the
> second STREAMON hangs. I guess the bug is not this driver. Probably
something
> in drivers/soc/rockchip/pm_domains.c. Just noting that in case you or
someone
> on Cc would like to investigate it further.
> I tested v4.16-rc4 on the Tinkerboard.
Looks like that version doesn't include the IOMMU PM and clock handling
rework [1], which should fix a lot of runtime PM issues. FWIW, linux-next
seems to already include it.
[1] https://lkml.org/lkml/2018/3/23/44
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:13 ` Tomasz Figa
@ 2018-05-07 6:38 ` Baruch Siach
2018-05-07 6:41 ` Tomasz Figa
0 siblings, 1 reply; 50+ messages in thread
From: Baruch Siach @ 2018-05-07 6:38 UTC (permalink / raw)
To: linux-arm-kernel
Hi Tomasz,
On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > +{
> > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > + int ret;
> > > +
> > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> on);
> > > +
> > > + if (on) {
> > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + rkisp1_config_clk(isp_dev);
> > > + } else {
> > > + ret = pm_runtime_put(isp_dev->dev);
>
> > I commented this line out to make more than one STREAMON work. Otherwise,
> > the second STREAMON hangs. I guess the bug is not this driver. Probably
> > something in drivers/soc/rockchip/pm_domains.c. Just noting that in case
> > you or someone on Cc would like to investigate it further.
> >
> > I tested v4.16-rc4 on the Tinkerboard.
>
> Looks like that version doesn't include the IOMMU PM and clock handling
> rework [1], which should fix a lot of runtime PM issues. FWIW, linux-next
> seems to already include it.
>
> [1] https://lkml.org/lkml/2018/3/23/44
Thanks for the reference.
It looks like the iommu driver part is in Linus' tree already. The DT part is
in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
Anyway, I'll take a look.
Thanks again,
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:38 ` Baruch Siach
@ 2018-05-07 6:41 ` Tomasz Figa
2018-05-24 11:30 ` Baruch Siach
0 siblings, 1 reply; 50+ messages in thread
From: Tomasz Figa @ 2018-05-07 6:41 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Tomasz,
> On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > > +{
> > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > + int ret;
> > > > +
> > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> > on);
> > > > +
> > > > + if (on) {
> > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > + if (ret < 0)
> > > > + return ret;
> > > > +
> > > > + rkisp1_config_clk(isp_dev);
> > > > + } else {
> > > > + ret = pm_runtime_put(isp_dev->dev);
> >
> > > I commented this line out to make more than one STREAMON work.
Otherwise,
> > > the second STREAMON hangs. I guess the bug is not this driver.
Probably
> > > something in drivers/soc/rockchip/pm_domains.c. Just noting that in
case
> > > you or someone on Cc would like to investigate it further.
> > >
> > > I tested v4.16-rc4 on the Tinkerboard.
> >
> > Looks like that version doesn't include the IOMMU PM and clock handling
> > rework [1], which should fix a lot of runtime PM issues. FWIW,
linux-next
> > seems to already include it.
> >
> > [1] https://lkml.org/lkml/2018/3/23/44
> Thanks for the reference.
> It looks like the iommu driver part is in Linus' tree already. The DT
part is
> in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
You're right, most of the series made it in time for 4.17. However, the DT
part (precisely, the clocks properties added to IOMMU nodes) is crucial for
the fixes to be effective.
> Anyway, I'll take a look.
Thanks for testing. :) (Forgot to mention in my previous email...)
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
@ 2018-05-16 5:20 ` Laurent Pinchart
2018-05-16 14:39 ` Jacob Chen
2019-03-10 17:49 ` Laurent Pinchart
1 sibling, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2018-05-16 5:20 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
Thank you for the patch.
On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
Should this really be a subdev driver ? After a quick look at the code, the
only parameters you need to configure the PHY is the number of lanes and the
data rate. Implementing the whole subdev API seems overcomplicated to me,
especially given that the D-PHY doesn't deal with video streams as such, but
operates one level down. Shouldn't we model the D-PHY using the Linux PHY
framework ? I believe all the features you need are there except for a D-PHY-
specific configuration function that should be very easy to add.
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> 2 files changed, 883 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
> index 000000000000..32140960557a
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -0,0 +1,868 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define RK3288_GRF_SOC_CON6 0x025c
> +#define RK3288_GRF_SOC_CON8 0x0264
> +#define RK3288_GRF_SOC_CON9 0x0268
> +#define RK3288_GRF_SOC_CON10 0x026c
> +#define RK3288_GRF_SOC_CON14 0x027c
> +#define RK3288_GRF_SOC_STATUS21 0x02d4
> +#define RK3288_GRF_IO_VSEL 0x0380
> +#define RK3288_GRF_SOC_CON15 0x03a4
> +
> +#define RK3399_GRF_SOC_CON9 0x6224
> +#define RK3399_GRF_SOC_CON21 0x6254
> +#define RK3399_GRF_SOC_CON22 0x6258
> +#define RK3399_GRF_SOC_CON23 0x625c
> +#define RK3399_GRF_SOC_CON24 0x6260
> +#define RK3399_GRF_SOC_CON25 0x6264
> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> +
> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> +#define LANE0_HS_RX_CONTROL 0x44
> +#define LANE1_HS_RX_CONTROL 0x54
> +#define LANE2_HS_RX_CONTROL 0x84
> +#define LANE3_HS_RX_CONTROL 0x94
> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> +
> +/*
> + * CSI HOST
> + */
> +#define CSIHOST_PHY_TEST_CTRL0 0x30
> +#define CSIHOST_PHY_TEST_CTRL1 0x34
> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> +#define CSIHOST_DPHY_RSTZ 0x0c
> +
> +#define PHY_TESTEN_ADDR (0x1 << 16)
> +#define PHY_TESTEN_DATA (0x0 << 16)
> +#define PHY_TESTCLK (0x1 << 1)
> +#define PHY_TESTCLR (0x1 << 0)
> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> +
> +#define HIWORD_UPDATE(val, mask, shift) \
> + ((val) << (shift) | (mask) << ((shift) + 16))
> +
> +enum mipi_dphy_sy_pads {
> + MIPI_DPHY_SY_PAD_SINK = 0,
> + MIPI_DPHY_SY_PAD_SOURCE,
> + MIPI_DPHY_SY_PADS_NUM,
> +};
> +
> +enum dphy_reg_id {
> + GRF_DPHY_RX0_TURNDISABLE = 0,
> + GRF_DPHY_RX0_FORCERXMODE,
> + GRF_DPHY_RX0_FORCETXSTOPMODE,
> + GRF_DPHY_RX0_ENABLE,
> + GRF_DPHY_RX0_TESTCLR,
> + GRF_DPHY_RX0_TESTCLK,
> + GRF_DPHY_RX0_TESTEN,
> + GRF_DPHY_RX0_TESTDIN,
> + GRF_DPHY_RX0_TURNREQUEST,
> + GRF_DPHY_RX0_TESTDOUT,
> + GRF_DPHY_TX0_TURNDISABLE,
> + GRF_DPHY_TX0_FORCERXMODE,
> + GRF_DPHY_TX0_FORCETXSTOPMODE,
> + GRF_DPHY_TX0_TURNREQUEST,
> + GRF_DPHY_TX1RX1_TURNDISABLE,
> + GRF_DPHY_TX1RX1_FORCERXMODE,
> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> + GRF_DPHY_TX1RX1_ENABLE,
> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> + GRF_DPHY_TX1RX1_BASEDIR,
> + GRF_DPHY_TX1RX1_ENABLECLK,
> + GRF_DPHY_TX1RX1_TURNREQUEST,
> + GRF_DPHY_RX1_SRC_SEL,
> + /* rk3288 only */
> + GRF_CON_DISABLE_ISP,
> + GRF_CON_ISP_DPHY_SEL,
> + GRF_DSI_CSI_TESTBUS_SEL,
> + GRF_DVP_V18SEL,
> + /* below is for rk3399 only */
> + GRF_DPHY_RX0_CLK_INV_SEL,
> + GRF_DPHY_RX1_CLK_INV_SEL,
> +};
> +
> +struct dphy_reg {
> + u32 offset;
> + u32 mask;
> + u32 shift;
> +};
> +
> +#define PHY_REG(_offset, _width, _shift) \
> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> +
> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> +};
> +
> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> +};
> +
> +struct hsfreq_range {
> + u32 range_h;
> + u8 cfg_bit;
> +};
> +
> +struct mipidphy_priv;
> +
> +struct dphy_drv_data {
> + const char * const *clks;
> + int num_clks;
> + const struct hsfreq_range *hsfreq_ranges;
> + int num_hsfreq_ranges;
> + const struct dphy_reg *regs;
> +};
> +
> +struct sensor_async_subdev {
> + struct v4l2_async_subdev asd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +#define MAX_DPHY_CLK 8
> +#define MAX_DPHY_SENSORS 2
> +
> +struct mipidphy_sensor {
> + struct v4l2_subdev *sd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +struct mipidphy_priv {
> + struct device *dev;
> + struct regmap *regmap_grf;
> + const struct dphy_reg *grf_regs;
> + struct clk *clks[MAX_DPHY_CLK];
> + const struct dphy_drv_data *drv_data;
> + u64 data_rate_mbps;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
> + int num_sensors;
> + bool is_streaming;
> + void __iomem *txrx_base_addr;
> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> +};
> +
> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
> *subdev) +{
> + return container_of(subdev, struct mipidphy_priv, sd);
> +}
> +
> +static inline void write_grf_reg(struct mipidphy_priv *priv,
> + int index, u8 value)
> +{
> + const struct dphy_reg *reg = &priv->grf_regs[index];
> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> +
> + WARN_ON(!reg->offset);
> + regmap_write(priv->regmap_grf, reg->offset, val);
> +}
> +
> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> + u8 test_code, u8 test_data)
> +{
> + /*
> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> + * is latched internally as the current test code. Test data is
> + * programmed internally by rising edge on TESTCLK.
> + */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> +}
> +
> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
> addr, + unsigned char data)
> +{
> + /*
> + * TESTEN =1,TESTDIN=addr
> + * TESTCLK=0
> + * TESTEN =0,TESTDIN=data
> + * TESTCLK=1
> + */
> + writel((PHY_TESTEN_ADDR | addr),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel((PHY_TESTEN_DATA | data),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> +}
> +
> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> +{
> + struct media_pad *local, *remote;
> + struct media_entity *sensor_me;
> +
> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> + remote = media_entity_remote_pad(local);
> + if (!remote) {
> + v4l2_warn(sd, "No link between dphy and sensor\n");
> + return NULL;
> + }
> +
> + sensor_me = media_entity_remote_pad(local)->entity;
> + return media_entity_to_v4l2_subdev(sensor_me);
> +}
> +
> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + int i;
> +
> + for (i = 0; i < priv->num_sensors; ++i)
> + if (priv->sensors[i].sd == sd)
> + return &priv->sensors[i];
> +
> + return NULL;
> +}
> +
> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> + int ret;
> +
> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
> + if (!link_freq) {
> + v4l2_warn(sd, "No pixel rate control in subdev\n");
> + return -EPIPE;
> + }
> +
> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> + if (ret < 0) {
> + v4l2_err(sd, "Failed to get menu item\n");
> + return ret;
> + }
> +
> + if (!qm.value) {
> + v4l2_err(sd, "Invalid link_freq\n");
> + return -EINVAL;
> + }
> + priv->data_rate_mbps = qm.value * 2;
> + do_div(priv->data_rate_mbps, 1000 * 1000);
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int ret = 0;
> +
> + if (priv->is_streaming)
> + return 0;
> +
> + ret = mipidphy_get_sensor_data_rate(sd);
> + if (ret < 0)
> + return ret;
> +
> + priv->stream_on(priv, sd);
> +
> + priv->is_streaming = true;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + priv->is_streaming = false;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> +{
> + if (on)
> + return mipidphy_s_stream_start(sd);
> + else
> + return mipidphy_s_stream_stop(sd);
> +}
> +
> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> + struct v4l2_mbus_config *config)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> +
> + *config = sensor->mbus;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + if (on)
> + return pm_runtime_get_sync(priv->dev);
> + else
> + return pm_runtime_put(priv->dev);
> +}
> +
> +static int mipidphy_runtime_suspend(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = num_clks - 1; i >= 0; i--)
> + clk_disable_unprepare(priv->clks[i]);
> +
> + return 0;
> +}
> +
> +static int mipidphy_runtime_resume(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks, ret;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = 0; i < num_clks; i++) {
> + ret = clk_prepare_enable(priv->clks[i]);
> + if (ret < 0)
> + goto err;
> + }
> +
> + return 0;
> +err:
> + while (--i >= 0)
> + clk_disable_unprepare(priv->clks[i]);
> + return ret;
> +}
> +
> +/* dphy accepts all fmt/size from sensor */
> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> +
> + /*
> + * Do not allow format changes and just relay whatever
> + * set currently in the sensor.
> + */
> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
> +}
> +
> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> + .set_fmt = mipidphy_get_set_fmt,
> + .get_fmt = mipidphy_get_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> + .s_power = mipidphy_s_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> + .g_mbus_config = mipidphy_g_mbus_config,
> + .s_stream = mipidphy_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> + .core = &mipidphy_core_ops,
> + .video = &mipidphy_video_ops,
> + .pad = &mipidphy_subdev_pad_ops,
> +};
> +
> +/* These tables must be sorted by .range_h ascending. */
> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}
> +};
> +
> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> +};
> +
> +static const char * const rk3399_mipidphy_clks[] = {
> + "dphy-ref",
> + "dphy-cfg",
> + "grf",
> +};
> +
> +static const char * const rk3288_mipidphy_clks[] = {
> + "dphy-ref",
> + "pclk",
> +};
> +
> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> +
> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> +
> + /* dphy start */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> + usleep_range(100, 150);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + /* HS hsfreq_range & lane 0 settle bypass */
> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane0 */
> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + /* HS RX Control of lane1 */
> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane2 */
> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane3 */
> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy0_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> + GENMASK(sensor->lanes - 1, 0));
> + /* dphy start */
> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy1_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> + .clks = rk3288_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> + .regs = rk3288_grf_dphy_regs,
> +};
> +
> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> + .clks = rk3399_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> + .regs = rk3399_grf_dphy_regs,
> +};
> +
> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> + {
> + .compatible = "rockchip,rk3399-mipi-dphy",
> + .data = &rk3399_mipidphy_drv_data,
> + },
> + {
> + .compatible = "rockchip,rk3288-mipi-dphy",
> + .data = &rk3288_mipidphy_drv_data,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
> +
> +/* The .bound() notifier callback when a match is found */
> +static int
> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct sensor_async_subdev *s_asd = container_of(asd,
> + struct sensor_async_subdev, asd);
> + struct mipidphy_sensor *sensor;
> + unsigned int pad, ret;
> +
> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> + return -EBUSY;
> +
> + sensor = &priv->sensors[priv->num_sensors++];
> + sensor->lanes = s_asd->lanes;
> + sensor->mbus = s_asd->mbus;
> + sensor->sd = sd;
> +
> + for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
> + if (sensor->sd->entity.pads[pad].flags
> + & MEDIA_PAD_FL_SOURCE)
> + break;
> +
> + if (pad == sensor->sd->entity.num_pads) {
> + dev_err(priv->dev,
> + "failed to find src pad for %s\n",
> + sensor->sd->name);
> +
> + return -ENXIO;
> + }
> +
> + ret = media_create_pad_link(
> + &sensor->sd->entity, pad,
> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to create link for %s\n",
> + sensor->sd->name);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* The .unbind callback */
> +static void
> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> +
> + sensor->sd = NULL;
> +}
> +
> +static const struct
> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> + .bound = rockchip_mipidphy_notifier_bound,
> + .unbind = rockchip_mipidphy_notifier_unbind,
> +};
> +
> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> + struct v4l2_fwnode_endpoint *vep,
> + struct v4l2_async_subdev *asd)
> +{
> + struct sensor_async_subdev *s_asd =
> + container_of(asd, struct sensor_async_subdev, asd);
> + struct v4l2_mbus_config *config = &s_asd->mbus;
> +
> + if (vep->bus_type != V4L2_MBUS_CSI2) {
> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> + return -EINVAL;
> + }
> +
> + if (vep->base.port != 0) {
> + dev_err(dev, "The PHY has only port 0\n");
> + return -EINVAL;
> + }
> +
> + config->type = V4L2_MBUS_CSI2;
> + config->flags = vep->bus.mipi_csi2.flags;
> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> +
> + switch (vep->bus.mipi_csi2.num_data_lanes) {
> + case 1:
> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> + break;
> + case 2:
> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> + break;
> + case 3:
> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> + break;
> + case 4:
> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> +{
> + int ret;
> +
> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> +
> + ret = media_entity_pads_init(&priv->sd.entity,
> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> + if (ret < 0)
> + return ret;
> +
> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> + priv->dev, &priv->notifier,
> + sizeof(struct sensor_async_subdev), 0,
> + rockchip_mipidphy_fwnode_parse);
> + if (ret < 0)
> + return ret;
> +
> + if (!priv->notifier.num_subdevs)
> + return -ENODEV; /* no endpoint */
> +
> + priv->sd.subdev_notifier = &priv->notifier;
> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to register async notifier : %d\n", ret);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + return v4l2_async_register_subdev(&priv->sd);
> +}
> +
> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct v4l2_subdev *sd;
> + struct mipidphy_priv *priv;
> + struct regmap *grf;
> + struct resource *res;
> + const struct of_device_id *of_id;
> + const struct dphy_drv_data *drv_data;
> + int i, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->dev = dev;
> +
> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + grf = syscon_node_to_regmap(dev->parent->of_node);
> + if (IS_ERR(grf)) {
> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(grf)) {
> + dev_err(dev, "Can't find GRF syscon\n");
> + return -ENODEV;
> + }
> + }
> + priv->regmap_grf = grf;
> +
> + drv_data = of_id->data;
> + for (i = 0; i < drv_data->num_clks; i++) {
> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> +
> + if (IS_ERR(priv->clks[i])) {
> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> + return PTR_ERR(priv->clks[i]);
> + }
> + }
> +
> + priv->grf_regs = drv_data->regs;
> + priv->drv_data = drv_data;
> + priv->stream_on = mipidphy_txrx_stream_on;
> + priv->txrx_base_addr = NULL;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
> + if (IS_ERR(priv->txrx_base_addr))
> + priv->stream_on = mipidphy_rx_stream_on;
> +
> + sd = &priv->sd;
> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> + sd->dev = dev;
> +
> + platform_set_drvdata(pdev, &sd->entity);
> +
> + ret = rockchip_mipidphy_media_init(priv);
> + if (ret < 0)
> + return ret;
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> +{
> + struct media_entity *me = platform_get_drvdata(pdev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> +
> + media_entity_cleanup(&sd->entity);
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> + mipidphy_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver rockchip_isp_mipidphy_driver = {
> + .probe = rockchip_mipidphy_probe,
> + .remove = rockchip_mipidphy_remove,
> + .driver = {
> + .name = "rockchip-sy-mipi-dphy",
> + .pm = &rockchip_mipidphy_pm_ops,
> + .of_match_table = rockchip_mipidphy_match_id,
> + },
> +};
> +
> +module_platform_driver(rockchip_isp_mipidphy_driver);
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
> index 000000000000..c558791064a2
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef __MIPI_DPHY_SY_H__
> +#define __MIPI_DPHY_SY_H__
> +
> +#include <media/v4l2-subdev.h>
> +
> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
> +
> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver
2018-03-08 9:47 ` [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver Jacob Chen
2018-03-11 6:58 ` Baruch Siach
@ 2018-05-16 8:08 ` Tomasz Figa
1 sibling, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-05-16 8:08 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob, Shunqian,
On Thu, Mar 8, 2018 at 6:49 PM Jacob Chen <jacob-chen@iotwrt.com> wrote:
[snip]
> +static const struct of_device_id rkisp1_plat_of_match[] = {
> + {
> + .compatible = "rockchip,rk3288-cif-isp",
> + .data = &rk3288_isp_clk_data,
> + }, {
> + .compatible = "rockchip,rk3399-cif-isp",
> + .data = &rk3399_isp_clk_data,
> + },
> + {},
> +};
We need MODULE_DEVICE_TABLE() here.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 5:20 ` Laurent Pinchart
@ 2018-05-16 14:39 ` Jacob Chen
2018-05-16 14:53 ` Jacob Chen
0 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-05-16 14:39 UTC (permalink / raw)
To: linux-arm-kernel
Hi Laurent,
2018-05-16 13:20 GMT+08:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
> Hi Jacob,
>
> Thank you for the patch.
>
> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
>> From: Jacob Chen <jacob2.chen@rock-chips.com>
>>
>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>
> Should this really be a subdev driver ? After a quick look at the code, the
> only parameters you need to configure the PHY is the number of lanes and the
> data rate. Implementing the whole subdev API seems overcomplicated to me,
> especially given that the D-PHY doesn't deal with video streams as such, but
> operates one level down. Shouldn't we model the D-PHY using the Linux PHY
> framework ? I believe all the features you need are there except for a D-PHY-
> specific configuration function that should be very easy to add.
>
It deserves a subdev driver since the ISP is not the only user.
Other driver, like VIP, use it too.
>> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
>> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
>> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
>> ---
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>> 2 files changed, 883 insertions(+)
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
>> index 000000000000..32140960557a
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> @@ -0,0 +1,868 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-fwnode.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define RK3288_GRF_SOC_CON6 0x025c
>> +#define RK3288_GRF_SOC_CON8 0x0264
>> +#define RK3288_GRF_SOC_CON9 0x0268
>> +#define RK3288_GRF_SOC_CON10 0x026c
>> +#define RK3288_GRF_SOC_CON14 0x027c
>> +#define RK3288_GRF_SOC_STATUS21 0x02d4
>> +#define RK3288_GRF_IO_VSEL 0x0380
>> +#define RK3288_GRF_SOC_CON15 0x03a4
>> +
>> +#define RK3399_GRF_SOC_CON9 0x6224
>> +#define RK3399_GRF_SOC_CON21 0x6254
>> +#define RK3399_GRF_SOC_CON22 0x6258
>> +#define RK3399_GRF_SOC_CON23 0x625c
>> +#define RK3399_GRF_SOC_CON24 0x6260
>> +#define RK3399_GRF_SOC_CON25 0x6264
>> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
>> +
>> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
>> +#define LANE0_HS_RX_CONTROL 0x44
>> +#define LANE1_HS_RX_CONTROL 0x54
>> +#define LANE2_HS_RX_CONTROL 0x84
>> +#define LANE3_HS_RX_CONTROL 0x94
>> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
>> +
>> +/*
>> + * CSI HOST
>> + */
>> +#define CSIHOST_PHY_TEST_CTRL0 0x30
>> +#define CSIHOST_PHY_TEST_CTRL1 0x34
>> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
>> +#define CSIHOST_DPHY_RSTZ 0x0c
>> +
>> +#define PHY_TESTEN_ADDR (0x1 << 16)
>> +#define PHY_TESTEN_DATA (0x0 << 16)
>> +#define PHY_TESTCLK (0x1 << 1)
>> +#define PHY_TESTCLR (0x1 << 0)
>> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
>> +
>> +#define HIWORD_UPDATE(val, mask, shift) \
>> + ((val) << (shift) | (mask) << ((shift) + 16))
>> +
>> +enum mipi_dphy_sy_pads {
>> + MIPI_DPHY_SY_PAD_SINK = 0,
>> + MIPI_DPHY_SY_PAD_SOURCE,
>> + MIPI_DPHY_SY_PADS_NUM,
>> +};
>> +
>> +enum dphy_reg_id {
>> + GRF_DPHY_RX0_TURNDISABLE = 0,
>> + GRF_DPHY_RX0_FORCERXMODE,
>> + GRF_DPHY_RX0_FORCETXSTOPMODE,
>> + GRF_DPHY_RX0_ENABLE,
>> + GRF_DPHY_RX0_TESTCLR,
>> + GRF_DPHY_RX0_TESTCLK,
>> + GRF_DPHY_RX0_TESTEN,
>> + GRF_DPHY_RX0_TESTDIN,
>> + GRF_DPHY_RX0_TURNREQUEST,
>> + GRF_DPHY_RX0_TESTDOUT,
>> + GRF_DPHY_TX0_TURNDISABLE,
>> + GRF_DPHY_TX0_FORCERXMODE,
>> + GRF_DPHY_TX0_FORCETXSTOPMODE,
>> + GRF_DPHY_TX0_TURNREQUEST,
>> + GRF_DPHY_TX1RX1_TURNDISABLE,
>> + GRF_DPHY_TX1RX1_FORCERXMODE,
>> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
>> + GRF_DPHY_TX1RX1_ENABLE,
>> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
>> + GRF_DPHY_TX1RX1_BASEDIR,
>> + GRF_DPHY_TX1RX1_ENABLECLK,
>> + GRF_DPHY_TX1RX1_TURNREQUEST,
>> + GRF_DPHY_RX1_SRC_SEL,
>> + /* rk3288 only */
>> + GRF_CON_DISABLE_ISP,
>> + GRF_CON_ISP_DPHY_SEL,
>> + GRF_DSI_CSI_TESTBUS_SEL,
>> + GRF_DVP_V18SEL,
>> + /* below is for rk3399 only */
>> + GRF_DPHY_RX0_CLK_INV_SEL,
>> + GRF_DPHY_RX1_CLK_INV_SEL,
>> +};
>> +
>> +struct dphy_reg {
>> + u32 offset;
>> + u32 mask;
>> + u32 shift;
>> +};
>> +
>> +#define PHY_REG(_offset, _width, _shift) \
>> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
>> +
>> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
>> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
>> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
>> +};
>> +
>> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
>> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
>> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
>> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
>> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
>> +};
>> +
>> +struct hsfreq_range {
>> + u32 range_h;
>> + u8 cfg_bit;
>> +};
>> +
>> +struct mipidphy_priv;
>> +
>> +struct dphy_drv_data {
>> + const char * const *clks;
>> + int num_clks;
>> + const struct hsfreq_range *hsfreq_ranges;
>> + int num_hsfreq_ranges;
>> + const struct dphy_reg *regs;
>> +};
>> +
>> +struct sensor_async_subdev {
>> + struct v4l2_async_subdev asd;
>> + struct v4l2_mbus_config mbus;
>> + int lanes;
>> +};
>> +
>> +#define MAX_DPHY_CLK 8
>> +#define MAX_DPHY_SENSORS 2
>> +
>> +struct mipidphy_sensor {
>> + struct v4l2_subdev *sd;
>> + struct v4l2_mbus_config mbus;
>> + int lanes;
>> +};
>> +
>> +struct mipidphy_priv {
>> + struct device *dev;
>> + struct regmap *regmap_grf;
>> + const struct dphy_reg *grf_regs;
>> + struct clk *clks[MAX_DPHY_CLK];
>> + const struct dphy_drv_data *drv_data;
>> + u64 data_rate_mbps;
>> + struct v4l2_async_notifier notifier;
>> + struct v4l2_subdev sd;
>> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
>> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>> + int num_sensors;
>> + bool is_streaming;
>> + void __iomem *txrx_base_addr;
>> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
>> +};
>> +
>> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
>> *subdev) +{
>> + return container_of(subdev, struct mipidphy_priv, sd);
>> +}
>> +
>> +static inline void write_grf_reg(struct mipidphy_priv *priv,
>> + int index, u8 value)
>> +{
>> + const struct dphy_reg *reg = &priv->grf_regs[index];
>> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
>> +
>> + WARN_ON(!reg->offset);
>> + regmap_write(priv->regmap_grf, reg->offset, val);
>> +}
>> +
>> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
>> + u8 test_code, u8 test_data)
>> +{
>> + /*
>> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
>> + * is latched internally as the current test code. Test data is
>> + * programmed internally by rising edge on TESTCLK.
>> + */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> +}
>> +
>> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
>> addr, + unsigned char data)
>> +{
>> + /*
>> + * TESTEN =1,TESTDIN=addr
>> + * TESTCLK=0
>> + * TESTEN =0,TESTDIN=data
>> + * TESTCLK=1
>> + */
>> + writel((PHY_TESTEN_ADDR | addr),
>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + writel((PHY_TESTEN_DATA | data),
>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +}
>> +
>> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
>> +{
>> + struct media_pad *local, *remote;
>> + struct media_entity *sensor_me;
>> +
>> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
>> + remote = media_entity_remote_pad(local);
>> + if (!remote) {
>> + v4l2_warn(sd, "No link between dphy and sensor\n");
>> + return NULL;
>> + }
>> +
>> + sensor_me = media_entity_remote_pad(local)->entity;
>> + return media_entity_to_v4l2_subdev(sensor_me);
>> +}
>> +
>> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < priv->num_sensors; ++i)
>> + if (priv->sensors[i].sd == sd)
>> + return &priv->sensors[i];
>> +
>> + return NULL;
>> +}
>> +
>> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct v4l2_ctrl *link_freq;
>> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
>> + int ret;
>> +
>> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>> + if (!link_freq) {
>> + v4l2_warn(sd, "No pixel rate control in subdev\n");
>> + return -EPIPE;
>> + }
>> +
>> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
>> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
>> + if (ret < 0) {
>> + v4l2_err(sd, "Failed to get menu item\n");
>> + return ret;
>> + }
>> +
>> + if (!qm.value) {
>> + v4l2_err(sd, "Invalid link_freq\n");
>> + return -EINVAL;
>> + }
>> + priv->data_rate_mbps = qm.value * 2;
>> + do_div(priv->data_rate_mbps, 1000 * 1000);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int ret = 0;
>> +
>> + if (priv->is_streaming)
>> + return 0;
>> +
>> + ret = mipidphy_get_sensor_data_rate(sd);
>> + if (ret < 0)
>> + return ret;
>> +
>> + priv->stream_on(priv, sd);
>> +
>> + priv->is_streaming = true;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> + priv->is_streaming = false;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> + if (on)
>> + return mipidphy_s_stream_start(sd);
>> + else
>> + return mipidphy_s_stream_stop(sd);
>> +}
>> +
>> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
>> + struct v4l2_mbus_config *config)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> +
>> + *config = sensor->mbus;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> + if (on)
>> + return pm_runtime_get_sync(priv->dev);
>> + else
>> + return pm_runtime_put(priv->dev);
>> +}
>> +
>> +static int mipidphy_runtime_suspend(struct device *dev)
>> +{
>> + struct media_entity *me = dev_get_drvdata(dev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int i, num_clks;
>> +
>> + num_clks = priv->drv_data->num_clks;
>> + for (i = num_clks - 1; i >= 0; i--)
>> + clk_disable_unprepare(priv->clks[i]);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_runtime_resume(struct device *dev)
>> +{
>> + struct media_entity *me = dev_get_drvdata(dev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int i, num_clks, ret;
>> +
>> + num_clks = priv->drv_data->num_clks;
>> + for (i = 0; i < num_clks; i++) {
>> + ret = clk_prepare_enable(priv->clks[i]);
>> + if (ret < 0)
>> + goto err;
>> + }
>> +
>> + return 0;
>> +err:
>> + while (--i >= 0)
>> + clk_disable_unprepare(priv->clks[i]);
>> + return ret;
>> +}
>> +
>> +/* dphy accepts all fmt/size from sensor */
>> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_format *fmt)
>> +{
>> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
>> +
>> + /*
>> + * Do not allow format changes and just relay whatever
>> + * set currently in the sensor.
>> + */
>> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>> +}
>> +
>> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
>> + .set_fmt = mipidphy_get_set_fmt,
>> + .get_fmt = mipidphy_get_set_fmt,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
>> + .s_power = mipidphy_s_power,
>> +};
>> +
>> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
>> + .g_mbus_config = mipidphy_g_mbus_config,
>> + .s_stream = mipidphy_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
>> + .core = &mipidphy_core_ops,
>> + .video = &mipidphy_video_ops,
>> + .pad = &mipidphy_subdev_pad_ops,
>> +};
>> +
>> +/* These tables must be sorted by .range_h ascending. */
>> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> + { 999, 0x1a}
>> +};
>> +
>> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
>> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
>> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
>> +};
>> +
>> +static const char * const rk3399_mipidphy_clks[] = {
>> + "dphy-ref",
>> + "dphy-cfg",
>> + "grf",
>> +};
>> +
>> +static const char * const rk3288_mipidphy_clks[] = {
>> + "dphy-ref",
>> + "pclk",
>> +};
>> +
>> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> + int i, hsfreq = 0;
>> +
>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>> + break;
>> + }
>> + }
>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
>> + /* Disable lan turn around, which is ignored in receive mode */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
>> +
>> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
>> +
>> + /* dphy start */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
>> + usleep_range(100, 150);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
>> + usleep_range(100, 150);
>> +
>> + /* set clock lane */
>> + /* HS hsfreq_range & lane 0 settle bypass */
>> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane0 */
>> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> + /* HS RX Control of lane1 */
>> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane2 */
>> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane3 */
>> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> + /* HS RX Data Lanes Settle State Time Control */
>> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> + THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> + /* Normal operation */
>> + mipidphy0_wr_reg(priv, 0x0, 0);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> + int i, hsfreq = 0;
>> +
>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>> + break;
>> + }
>> + }
>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
>> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
>> + /* Disable lan turn around, which is ignored in receive mode */
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
>> + GENMASK(sensor->lanes - 1, 0));
>> + /* dphy start */
>> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
>> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + usleep_range(100, 150);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + usleep_range(100, 150);
>> +
>> + /* set clock lane */
>> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> + /* HS RX Data Lanes Settle State Time Control */
>> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> + THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> + /* Normal operation */
>> + mipidphy1_wr_reg(priv, 0x0, 0);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
>> + .clks = rk3288_mipidphy_clks,
>> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
>> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
>> + .regs = rk3288_grf_dphy_regs,
>> +};
>> +
>> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
>> + .clks = rk3399_mipidphy_clks,
>> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
>> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
>> + .regs = rk3399_grf_dphy_regs,
>> +};
>> +
>> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
>> + {
>> + .compatible = "rockchip,rk3399-mipi-dphy",
>> + .data = &rk3399_mipidphy_drv_data,
>> + },
>> + {
>> + .compatible = "rockchip,rk3288-mipi-dphy",
>> + .data = &rk3288_mipidphy_drv_data,
>> + },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>> +
>> +/* The .bound() notifier callback when a match is found */
>> +static int
>> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
>> + struct v4l2_subdev *sd,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct mipidphy_priv *priv = container_of(notifier,
>> + struct mipidphy_priv,
>> + notifier);
>> + struct sensor_async_subdev *s_asd = container_of(asd,
>> + struct sensor_async_subdev, asd);
>> + struct mipidphy_sensor *sensor;
>> + unsigned int pad, ret;
>> +
>> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
>> + return -EBUSY;
>> +
>> + sensor = &priv->sensors[priv->num_sensors++];
>> + sensor->lanes = s_asd->lanes;
>> + sensor->mbus = s_asd->mbus;
>> + sensor->sd = sd;
>> +
>> + for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
>> + if (sensor->sd->entity.pads[pad].flags
>> + & MEDIA_PAD_FL_SOURCE)
>> + break;
>> +
>> + if (pad == sensor->sd->entity.num_pads) {
>> + dev_err(priv->dev,
>> + "failed to find src pad for %s\n",
>> + sensor->sd->name);
>> +
>> + return -ENXIO;
>> + }
>> +
>> + ret = media_create_pad_link(
>> + &sensor->sd->entity, pad,
>> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
>> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
>> + if (ret) {
>> + dev_err(priv->dev,
>> + "failed to create link for %s\n",
>> + sensor->sd->name);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* The .unbind callback */
>> +static void
>> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
>> + struct v4l2_subdev *sd,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct mipidphy_priv *priv = container_of(notifier,
>> + struct mipidphy_priv,
>> + notifier);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
>> +
>> + sensor->sd = NULL;
>> +}
>> +
>> +static const struct
>> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
>> + .bound = rockchip_mipidphy_notifier_bound,
>> + .unbind = rockchip_mipidphy_notifier_unbind,
>> +};
>> +
>> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
>> + struct v4l2_fwnode_endpoint *vep,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct sensor_async_subdev *s_asd =
>> + container_of(asd, struct sensor_async_subdev, asd);
>> + struct v4l2_mbus_config *config = &s_asd->mbus;
>> +
>> + if (vep->bus_type != V4L2_MBUS_CSI2) {
>> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
>> + return -EINVAL;
>> + }
>> +
>> + if (vep->base.port != 0) {
>> + dev_err(dev, "The PHY has only port 0\n");
>> + return -EINVAL;
>> + }
>> +
>> + config->type = V4L2_MBUS_CSI2;
>> + config->flags = vep->bus.mipi_csi2.flags;
>> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>> +
>> + switch (vep->bus.mipi_csi2.num_data_lanes) {
>> + case 1:
>> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
>> + break;
>> + case 2:
>> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
>> + break;
>> + case 3:
>> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
>> + break;
>> + case 4:
>> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
>> +{
>> + int ret;
>> +
>> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
>> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
>> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
>> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
>> +
>> + ret = media_entity_pads_init(&priv->sd.entity,
>> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
>> + priv->dev, &priv->notifier,
>> + sizeof(struct sensor_async_subdev), 0,
>> + rockchip_mipidphy_fwnode_parse);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (!priv->notifier.num_subdevs)
>> + return -ENODEV; /* no endpoint */
>> +
>> + priv->sd.subdev_notifier = &priv->notifier;
>> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
>> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
>> + if (ret) {
>> + dev_err(priv->dev,
>> + "failed to register async notifier : %d\n", ret);
>> + v4l2_async_notifier_cleanup(&priv->notifier);
>> + return ret;
>> + }
>> +
>> + return v4l2_async_register_subdev(&priv->sd);
>> +}
>> +
>> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct v4l2_subdev *sd;
>> + struct mipidphy_priv *priv;
>> + struct regmap *grf;
>> + struct resource *res;
>> + const struct of_device_id *of_id;
>> + const struct dphy_drv_data *drv_data;
>> + int i, ret;
>> +
>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> + priv->dev = dev;
>> +
>> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
>> + if (!of_id)
>> + return -EINVAL;
>> +
>> + grf = syscon_node_to_regmap(dev->parent->of_node);
>> + if (IS_ERR(grf)) {
>> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
>> + "rockchip,grf");
>> + if (IS_ERR(grf)) {
>> + dev_err(dev, "Can't find GRF syscon\n");
>> + return -ENODEV;
>> + }
>> + }
>> + priv->regmap_grf = grf;
>> +
>> + drv_data = of_id->data;
>> + for (i = 0; i < drv_data->num_clks; i++) {
>> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
>> +
>> + if (IS_ERR(priv->clks[i])) {
>> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
>> + return PTR_ERR(priv->clks[i]);
>> + }
>> + }
>> +
>> + priv->grf_regs = drv_data->regs;
>> + priv->drv_data = drv_data;
>> + priv->stream_on = mipidphy_txrx_stream_on;
>> + priv->txrx_base_addr = NULL;
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(priv->txrx_base_addr))
>> + priv->stream_on = mipidphy_rx_stream_on;
>> +
>> + sd = &priv->sd;
>> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
>> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
>> + sd->dev = dev;
>> +
>> + platform_set_drvdata(pdev, &sd->entity);
>> +
>> + ret = rockchip_mipidphy_media_init(priv);
>> + if (ret < 0)
>> + return ret;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
>> +{
>> + struct media_entity *me = platform_get_drvdata(pdev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> +
>> + media_entity_cleanup(&sd->entity);
>> +
>> + pm_runtime_disable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
>> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
>> + mipidphy_runtime_resume, NULL)
>> +};
>> +
>> +static struct platform_driver rockchip_isp_mipidphy_driver = {
>> + .probe = rockchip_mipidphy_probe,
>> + .remove = rockchip_mipidphy_remove,
>> + .driver = {
>> + .name = "rockchip-sy-mipi-dphy",
>> + .pm = &rockchip_mipidphy_pm_ops,
>> + .of_match_table = rockchip_mipidphy_match_id,
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_isp_mipidphy_driver);
>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
>> +MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
>> index 000000000000..c558791064a2
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> @@ -0,0 +1,15 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#ifndef __MIPI_DPHY_SY_H__
>> +#define __MIPI_DPHY_SY_H__
>> +
>> +#include <media/v4l2-subdev.h>
>> +
>> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>> +
>> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>
>
> --
> Regards,
>
> Laurent Pinchart
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 14:39 ` Jacob Chen
@ 2018-05-16 14:53 ` Jacob Chen
2018-05-16 15:15 ` Tomasz Figa
0 siblings, 1 reply; 50+ messages in thread
From: Jacob Chen @ 2018-05-16 14:53 UTC (permalink / raw)
To: linux-arm-kernel
2018-05-16 22:39 GMT+08:00 Jacob Chen <jacobchen110@gmail.com>:
> Hi Laurent,
>
> 2018-05-16 13:20 GMT+08:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
>> Hi Jacob,
>>
>> Thank you for the patch.
>>
>> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
>>> From: Jacob Chen <jacob2.chen@rock-chips.com>
>>>
>>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>>
>> Should this really be a subdev driver ? After a quick look at the code, the
>> only parameters you need to configure the PHY is the number of lanes and the
>> data rate. Implementing the whole subdev API seems overcomplicated to me,
>> especially given that the D-PHY doesn't deal with video streams as such, but
>> operates one level down. Shouldn't we model the D-PHY using the Linux PHY
>> framework ? I believe all the features you need are there except for a D-PHY-
>> specific configuration function that should be very easy to add.
>>
>
> It deserves a subdev driver since the ISP is not the only user.
> Other driver, like VIP, use it too.
>
>
For example, if there are two sensors connected to a rk3399 board.
Sensor1 --> DPHY1
Sensor2 --> DPHY2
With a subdev phy driver, i can choose either ISP or VIP for
sensor1/sensor2 by enable/disable media link in the run time.
1.
Sensor1 --> DPHY1 ---> VIP
Sensor2 --> DPHY2 ---> ISP1
2.
Sensor1 --> DPHY1 ---> ISP1
Sensor2 --> DPHY2 ---> VIP
>>> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
>>> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
>>> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
>>> ---
>>> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
>>> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>>> 2 files changed, 883 insertions(+)
>>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>>
>>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
>>> index 000000000000..32140960557a
>>> --- /dev/null
>>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> @@ -0,0 +1,868 @@
>>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>>> +/*
>>> + * Rockchip MIPI Synopsys DPHY driver
>>> + *
>>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/mfd/syscon.h>
>>> +#include <media/media-entity.h>
>>> +#include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-fwnode.h>
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +#define RK3288_GRF_SOC_CON6 0x025c
>>> +#define RK3288_GRF_SOC_CON8 0x0264
>>> +#define RK3288_GRF_SOC_CON9 0x0268
>>> +#define RK3288_GRF_SOC_CON10 0x026c
>>> +#define RK3288_GRF_SOC_CON14 0x027c
>>> +#define RK3288_GRF_SOC_STATUS21 0x02d4
>>> +#define RK3288_GRF_IO_VSEL 0x0380
>>> +#define RK3288_GRF_SOC_CON15 0x03a4
>>> +
>>> +#define RK3399_GRF_SOC_CON9 0x6224
>>> +#define RK3399_GRF_SOC_CON21 0x6254
>>> +#define RK3399_GRF_SOC_CON22 0x6258
>>> +#define RK3399_GRF_SOC_CON23 0x625c
>>> +#define RK3399_GRF_SOC_CON24 0x6260
>>> +#define RK3399_GRF_SOC_CON25 0x6264
>>> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
>>> +
>>> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
>>> +#define LANE0_HS_RX_CONTROL 0x44
>>> +#define LANE1_HS_RX_CONTROL 0x54
>>> +#define LANE2_HS_RX_CONTROL 0x84
>>> +#define LANE3_HS_RX_CONTROL 0x94
>>> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
>>> +
>>> +/*
>>> + * CSI HOST
>>> + */
>>> +#define CSIHOST_PHY_TEST_CTRL0 0x30
>>> +#define CSIHOST_PHY_TEST_CTRL1 0x34
>>> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
>>> +#define CSIHOST_DPHY_RSTZ 0x0c
>>> +
>>> +#define PHY_TESTEN_ADDR (0x1 << 16)
>>> +#define PHY_TESTEN_DATA (0x0 << 16)
>>> +#define PHY_TESTCLK (0x1 << 1)
>>> +#define PHY_TESTCLR (0x1 << 0)
>>> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
>>> +
>>> +#define HIWORD_UPDATE(val, mask, shift) \
>>> + ((val) << (shift) | (mask) << ((shift) + 16))
>>> +
>>> +enum mipi_dphy_sy_pads {
>>> + MIPI_DPHY_SY_PAD_SINK = 0,
>>> + MIPI_DPHY_SY_PAD_SOURCE,
>>> + MIPI_DPHY_SY_PADS_NUM,
>>> +};
>>> +
>>> +enum dphy_reg_id {
>>> + GRF_DPHY_RX0_TURNDISABLE = 0,
>>> + GRF_DPHY_RX0_FORCERXMODE,
>>> + GRF_DPHY_RX0_FORCETXSTOPMODE,
>>> + GRF_DPHY_RX0_ENABLE,
>>> + GRF_DPHY_RX0_TESTCLR,
>>> + GRF_DPHY_RX0_TESTCLK,
>>> + GRF_DPHY_RX0_TESTEN,
>>> + GRF_DPHY_RX0_TESTDIN,
>>> + GRF_DPHY_RX0_TURNREQUEST,
>>> + GRF_DPHY_RX0_TESTDOUT,
>>> + GRF_DPHY_TX0_TURNDISABLE,
>>> + GRF_DPHY_TX0_FORCERXMODE,
>>> + GRF_DPHY_TX0_FORCETXSTOPMODE,
>>> + GRF_DPHY_TX0_TURNREQUEST,
>>> + GRF_DPHY_TX1RX1_TURNDISABLE,
>>> + GRF_DPHY_TX1RX1_FORCERXMODE,
>>> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
>>> + GRF_DPHY_TX1RX1_ENABLE,
>>> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
>>> + GRF_DPHY_TX1RX1_BASEDIR,
>>> + GRF_DPHY_TX1RX1_ENABLECLK,
>>> + GRF_DPHY_TX1RX1_TURNREQUEST,
>>> + GRF_DPHY_RX1_SRC_SEL,
>>> + /* rk3288 only */
>>> + GRF_CON_DISABLE_ISP,
>>> + GRF_CON_ISP_DPHY_SEL,
>>> + GRF_DSI_CSI_TESTBUS_SEL,
>>> + GRF_DVP_V18SEL,
>>> + /* below is for rk3399 only */
>>> + GRF_DPHY_RX0_CLK_INV_SEL,
>>> + GRF_DPHY_RX1_CLK_INV_SEL,
>>> +};
>>> +
>>> +struct dphy_reg {
>>> + u32 offset;
>>> + u32 mask;
>>> + u32 shift;
>>> +};
>>> +
>>> +#define PHY_REG(_offset, _width, _shift) \
>>> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
>>> +
>>> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
>>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
>>> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
>>> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
>>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
>>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
>>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
>>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
>>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
>>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
>>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
>>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
>>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
>>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
>>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
>>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
>>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
>>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
>>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
>>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
>>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
>>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
>>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
>>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
>>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
>>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
>>> +};
>>> +
>>> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
>>> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
>>> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
>>> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
>>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
>>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
>>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
>>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
>>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
>>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
>>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
>>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
>>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
>>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
>>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
>>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
>>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
>>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
>>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
>>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
>>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
>>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
>>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
>>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
>>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
>>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
>>> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
>>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
>>> +};
>>> +
>>> +struct hsfreq_range {
>>> + u32 range_h;
>>> + u8 cfg_bit;
>>> +};
>>> +
>>> +struct mipidphy_priv;
>>> +
>>> +struct dphy_drv_data {
>>> + const char * const *clks;
>>> + int num_clks;
>>> + const struct hsfreq_range *hsfreq_ranges;
>>> + int num_hsfreq_ranges;
>>> + const struct dphy_reg *regs;
>>> +};
>>> +
>>> +struct sensor_async_subdev {
>>> + struct v4l2_async_subdev asd;
>>> + struct v4l2_mbus_config mbus;
>>> + int lanes;
>>> +};
>>> +
>>> +#define MAX_DPHY_CLK 8
>>> +#define MAX_DPHY_SENSORS 2
>>> +
>>> +struct mipidphy_sensor {
>>> + struct v4l2_subdev *sd;
>>> + struct v4l2_mbus_config mbus;
>>> + int lanes;
>>> +};
>>> +
>>> +struct mipidphy_priv {
>>> + struct device *dev;
>>> + struct regmap *regmap_grf;
>>> + const struct dphy_reg *grf_regs;
>>> + struct clk *clks[MAX_DPHY_CLK];
>>> + const struct dphy_drv_data *drv_data;
>>> + u64 data_rate_mbps;
>>> + struct v4l2_async_notifier notifier;
>>> + struct v4l2_subdev sd;
>>> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
>>> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>>> + int num_sensors;
>>> + bool is_streaming;
>>> + void __iomem *txrx_base_addr;
>>> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
>>> +};
>>> +
>>> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
>>> *subdev) +{
>>> + return container_of(subdev, struct mipidphy_priv, sd);
>>> +}
>>> +
>>> +static inline void write_grf_reg(struct mipidphy_priv *priv,
>>> + int index, u8 value)
>>> +{
>>> + const struct dphy_reg *reg = &priv->grf_regs[index];
>>> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
>>> +
>>> + WARN_ON(!reg->offset);
>>> + regmap_write(priv->regmap_grf, reg->offset, val);
>>> +}
>>> +
>>> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
>>> + u8 test_code, u8 test_data)
>>> +{
>>> + /*
>>> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
>>> + * is latched internally as the current test code. Test data is
>>> + * programmed internally by rising edge on TESTCLK.
>>> + */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> +}
>>> +
>>> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
>>> addr, + unsigned char data)
>>> +{
>>> + /*
>>> + * TESTEN =1,TESTDIN=addr
>>> + * TESTCLK=0
>>> + * TESTEN =0,TESTDIN=data
>>> + * TESTCLK=1
>>> + */
>>> + writel((PHY_TESTEN_ADDR | addr),
>>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>>> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + writel((PHY_TESTEN_DATA | data),
>>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> +}
>>> +
>>> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
>>> +{
>>> + struct media_pad *local, *remote;
>>> + struct media_entity *sensor_me;
>>> +
>>> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
>>> + remote = media_entity_remote_pad(local);
>>> + if (!remote) {
>>> + v4l2_warn(sd, "No link between dphy and sensor\n");
>>> + return NULL;
>>> + }
>>> +
>>> + sensor_me = media_entity_remote_pad(local)->entity;
>>> + return media_entity_to_v4l2_subdev(sensor_me);
>>> +}
>>> +
>>> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < priv->num_sensors; ++i)
>>> + if (priv->sensors[i].sd == sd)
>>> + return &priv->sensors[i];
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct v4l2_ctrl *link_freq;
>>> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
>>> + int ret;
>>> +
>>> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>>> + if (!link_freq) {
>>> + v4l2_warn(sd, "No pixel rate control in subdev\n");
>>> + return -EPIPE;
>>> + }
>>> +
>>> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
>>> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
>>> + if (ret < 0) {
>>> + v4l2_err(sd, "Failed to get menu item\n");
>>> + return ret;
>>> + }
>>> +
>>> + if (!qm.value) {
>>> + v4l2_err(sd, "Invalid link_freq\n");
>>> + return -EINVAL;
>>> + }
>>> + priv->data_rate_mbps = qm.value * 2;
>>> + do_div(priv->data_rate_mbps, 1000 * 1000);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int ret = 0;
>>> +
>>> + if (priv->is_streaming)
>>> + return 0;
>>> +
>>> + ret = mipidphy_get_sensor_data_rate(sd);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + priv->stream_on(priv, sd);
>>> +
>>> + priv->is_streaming = true;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> +
>>> + priv->is_streaming = false;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
>>> +{
>>> + if (on)
>>> + return mipidphy_s_stream_start(sd);
>>> + else
>>> + return mipidphy_s_stream_stop(sd);
>>> +}
>>> +
>>> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
>>> + struct v4l2_mbus_config *config)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> +
>>> + *config = sensor->mbus;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> +
>>> + if (on)
>>> + return pm_runtime_get_sync(priv->dev);
>>> + else
>>> + return pm_runtime_put(priv->dev);
>>> +}
>>> +
>>> +static int mipidphy_runtime_suspend(struct device *dev)
>>> +{
>>> + struct media_entity *me = dev_get_drvdata(dev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int i, num_clks;
>>> +
>>> + num_clks = priv->drv_data->num_clks;
>>> + for (i = num_clks - 1; i >= 0; i--)
>>> + clk_disable_unprepare(priv->clks[i]);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_runtime_resume(struct device *dev)
>>> +{
>>> + struct media_entity *me = dev_get_drvdata(dev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int i, num_clks, ret;
>>> +
>>> + num_clks = priv->drv_data->num_clks;
>>> + for (i = 0; i < num_clks; i++) {
>>> + ret = clk_prepare_enable(priv->clks[i]);
>>> + if (ret < 0)
>>> + goto err;
>>> + }
>>> +
>>> + return 0;
>>> +err:
>>> + while (--i >= 0)
>>> + clk_disable_unprepare(priv->clks[i]);
>>> + return ret;
>>> +}
>>> +
>>> +/* dphy accepts all fmt/size from sensor */
>>> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
>>> + struct v4l2_subdev_pad_config *cfg,
>>> + struct v4l2_subdev_format *fmt)
>>> +{
>>> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
>>> +
>>> + /*
>>> + * Do not allow format changes and just relay whatever
>>> + * set currently in the sensor.
>>> + */
>>> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>>> +}
>>> +
>>> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
>>> + .set_fmt = mipidphy_get_set_fmt,
>>> + .get_fmt = mipidphy_get_set_fmt,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
>>> + .s_power = mipidphy_s_power,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
>>> + .g_mbus_config = mipidphy_g_mbus_config,
>>> + .s_stream = mipidphy_s_stream,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
>>> + .core = &mipidphy_core_ops,
>>> + .video = &mipidphy_video_ops,
>>> + .pad = &mipidphy_subdev_pad_ops,
>>> +};
>>> +
>>> +/* These tables must be sorted by .range_h ascending. */
>>> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
>>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>>> + { 999, 0x1a}
>>> +};
>>> +
>>> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
>>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>>> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
>>> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
>>> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
>>> +};
>>> +
>>> +static const char * const rk3399_mipidphy_clks[] = {
>>> + "dphy-ref",
>>> + "dphy-cfg",
>>> + "grf",
>>> +};
>>> +
>>> +static const char * const rk3288_mipidphy_clks[] = {
>>> + "dphy-ref",
>>> + "pclk",
>>> +};
>>> +
>>> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>>> + int i, hsfreq = 0;
>>> +
>>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>>> + break;
>>> + }
>>> + }
>>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
>>> + /* Disable lan turn around, which is ignored in receive mode */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
>>> +
>>> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
>>> +
>>> + /* dphy start */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
>>> + usleep_range(100, 150);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
>>> + usleep_range(100, 150);
>>> +
>>> + /* set clock lane */
>>> + /* HS hsfreq_range & lane 0 settle bypass */
>>> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane0 */
>>> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>>> + /* HS RX Control of lane1 */
>>> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane2 */
>>> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane3 */
>>> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>>> + /* HS RX Data Lanes Settle State Time Control */
>>> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>>> + THS_SETTLE_COUNTER_THRESHOLD);
>>> +
>>> + /* Normal operation */
>>> + mipidphy0_wr_reg(priv, 0x0, 0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>>> + int i, hsfreq = 0;
>>> +
>>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>>> + break;
>>> + }
>>> + }
>>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
>>> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
>>> + /* Disable lan turn around, which is ignored in receive mode */
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
>>> + GENMASK(sensor->lanes - 1, 0));
>>> + /* dphy start */
>>> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
>>> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + usleep_range(100, 150);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + usleep_range(100, 150);
>>> +
>>> + /* set clock lane */
>>> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>>> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>>> + /* HS RX Data Lanes Settle State Time Control */
>>> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>>> + THS_SETTLE_COUNTER_THRESHOLD);
>>> +
>>> + /* Normal operation */
>>> + mipidphy1_wr_reg(priv, 0x0, 0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
>>> + .clks = rk3288_mipidphy_clks,
>>> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
>>> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
>>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
>>> + .regs = rk3288_grf_dphy_regs,
>>> +};
>>> +
>>> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
>>> + .clks = rk3399_mipidphy_clks,
>>> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
>>> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
>>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
>>> + .regs = rk3399_grf_dphy_regs,
>>> +};
>>> +
>>> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
>>> + {
>>> + .compatible = "rockchip,rk3399-mipi-dphy",
>>> + .data = &rk3399_mipidphy_drv_data,
>>> + },
>>> + {
>>> + .compatible = "rockchip,rk3288-mipi-dphy",
>>> + .data = &rk3288_mipidphy_drv_data,
>>> + },
>>> + {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>>> +
>>> +/* The .bound() notifier callback when a match is found */
>>> +static int
>>> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
>>> + struct v4l2_subdev *sd,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct mipidphy_priv *priv = container_of(notifier,
>>> + struct mipidphy_priv,
>>> + notifier);
>>> + struct sensor_async_subdev *s_asd = container_of(asd,
>>> + struct sensor_async_subdev, asd);
>>> + struct mipidphy_sensor *sensor;
>>> + unsigned int pad, ret;
>>> +
>>> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
>>> + return -EBUSY;
>>> +
>>> + sensor = &priv->sensors[priv->num_sensors++];
>>> + sensor->lanes = s_asd->lanes;
>>> + sensor->mbus = s_asd->mbus;
>>> + sensor->sd = sd;
>>> +
>>> + for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
>>> + if (sensor->sd->entity.pads[pad].flags
>>> + & MEDIA_PAD_FL_SOURCE)
>>> + break;
>>> +
>>> + if (pad == sensor->sd->entity.num_pads) {
>>> + dev_err(priv->dev,
>>> + "failed to find src pad for %s\n",
>>> + sensor->sd->name);
>>> +
>>> + return -ENXIO;
>>> + }
>>> +
>>> + ret = media_create_pad_link(
>>> + &sensor->sd->entity, pad,
>>> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
>>> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
>>> + if (ret) {
>>> + dev_err(priv->dev,
>>> + "failed to create link for %s\n",
>>> + sensor->sd->name);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* The .unbind callback */
>>> +static void
>>> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
>>> + struct v4l2_subdev *sd,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct mipidphy_priv *priv = container_of(notifier,
>>> + struct mipidphy_priv,
>>> + notifier);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
>>> +
>>> + sensor->sd = NULL;
>>> +}
>>> +
>>> +static const struct
>>> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
>>> + .bound = rockchip_mipidphy_notifier_bound,
>>> + .unbind = rockchip_mipidphy_notifier_unbind,
>>> +};
>>> +
>>> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
>>> + struct v4l2_fwnode_endpoint *vep,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct sensor_async_subdev *s_asd =
>>> + container_of(asd, struct sensor_async_subdev, asd);
>>> + struct v4l2_mbus_config *config = &s_asd->mbus;
>>> +
>>> + if (vep->bus_type != V4L2_MBUS_CSI2) {
>>> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + if (vep->base.port != 0) {
>>> + dev_err(dev, "The PHY has only port 0\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + config->type = V4L2_MBUS_CSI2;
>>> + config->flags = vep->bus.mipi_csi2.flags;
>>> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>>> +
>>> + switch (vep->bus.mipi_csi2.num_data_lanes) {
>>> + case 1:
>>> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
>>> + break;
>>> + case 2:
>>> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
>>> + break;
>>> + case 3:
>>> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
>>> + break;
>>> + case 4:
>>> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
>>> + break;
>>> + default:
>>> + return -EINVAL;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
>>> +{
>>> + int ret;
>>> +
>>> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
>>> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
>>> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
>>> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
>>> +
>>> + ret = media_entity_pads_init(&priv->sd.entity,
>>> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
>>> + priv->dev, &priv->notifier,
>>> + sizeof(struct sensor_async_subdev), 0,
>>> + rockchip_mipidphy_fwnode_parse);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + if (!priv->notifier.num_subdevs)
>>> + return -ENODEV; /* no endpoint */
>>> +
>>> + priv->sd.subdev_notifier = &priv->notifier;
>>> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
>>> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
>>> + if (ret) {
>>> + dev_err(priv->dev,
>>> + "failed to register async notifier : %d\n", ret);
>>> + v4l2_async_notifier_cleanup(&priv->notifier);
>>> + return ret;
>>> + }
>>> +
>>> + return v4l2_async_register_subdev(&priv->sd);
>>> +}
>>> +
>>> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
>>> +{
>>> + struct device *dev = &pdev->dev;
>>> + struct v4l2_subdev *sd;
>>> + struct mipidphy_priv *priv;
>>> + struct regmap *grf;
>>> + struct resource *res;
>>> + const struct of_device_id *of_id;
>>> + const struct dphy_drv_data *drv_data;
>>> + int i, ret;
>>> +
>>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> + priv->dev = dev;
>>> +
>>> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
>>> + if (!of_id)
>>> + return -EINVAL;
>>> +
>>> + grf = syscon_node_to_regmap(dev->parent->of_node);
>>> + if (IS_ERR(grf)) {
>>> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
>>> + "rockchip,grf");
>>> + if (IS_ERR(grf)) {
>>> + dev_err(dev, "Can't find GRF syscon\n");
>>> + return -ENODEV;
>>> + }
>>> + }
>>> + priv->regmap_grf = grf;
>>> +
>>> + drv_data = of_id->data;
>>> + for (i = 0; i < drv_data->num_clks; i++) {
>>> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
>>> +
>>> + if (IS_ERR(priv->clks[i])) {
>>> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
>>> + return PTR_ERR(priv->clks[i]);
>>> + }
>>> + }
>>> +
>>> + priv->grf_regs = drv_data->regs;
>>> + priv->drv_data = drv_data;
>>> + priv->stream_on = mipidphy_txrx_stream_on;
>>> + priv->txrx_base_addr = NULL;
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>>> + if (IS_ERR(priv->txrx_base_addr))
>>> + priv->stream_on = mipidphy_rx_stream_on;
>>> +
>>> + sd = &priv->sd;
>>> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
>>> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
>>> + sd->dev = dev;
>>> +
>>> + platform_set_drvdata(pdev, &sd->entity);
>>> +
>>> + ret = rockchip_mipidphy_media_init(priv);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
>>> +{
>>> + struct media_entity *me = platform_get_drvdata(pdev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> +
>>> + media_entity_cleanup(&sd->entity);
>>> +
>>> + pm_runtime_disable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
>>> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
>>> + mipidphy_runtime_resume, NULL)
>>> +};
>>> +
>>> +static struct platform_driver rockchip_isp_mipidphy_driver = {
>>> + .probe = rockchip_mipidphy_probe,
>>> + .remove = rockchip_mipidphy_remove,
>>> + .driver = {
>>> + .name = "rockchip-sy-mipi-dphy",
>>> + .pm = &rockchip_mipidphy_pm_ops,
>>> + .of_match_table = rockchip_mipidphy_match_id,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(rockchip_isp_mipidphy_driver);
>>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>>> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
>>> +MODULE_LICENSE("Dual BSD/GPL");
>>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
>>> index 000000000000..c558791064a2
>>> --- /dev/null
>>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>> @@ -0,0 +1,15 @@
>>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>>> +/*
>>> + * Rockchip MIPI Synopsys DPHY driver
>>> + *
>>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>>> + */
>>> +
>>> +#ifndef __MIPI_DPHY_SY_H__
>>> +#define __MIPI_DPHY_SY_H__
>>> +
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>>> +
>>> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>>
>>
>> --
>> Regards,
>>
>> Laurent Pinchart
>>
>>
>>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 14:53 ` Jacob Chen
@ 2018-05-16 15:15 ` Tomasz Figa
0 siblings, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-05-16 15:15 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
On Wed, May 16, 2018 at 11:54 PM Jacob Chen <jacobchen110@gmail.com> wrote:
> 2018-05-16 22:39 GMT+08:00 Jacob Chen <jacobchen110@gmail.com>:
> > Hi Laurent,
> >
> > 2018-05-16 13:20 GMT+08:00 Laurent Pinchart <
laurent.pinchart@ideasonboard.com>:
> >> Hi Jacob,
> >>
> >> Thank you for the patch.
> >>
> >> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
> >>> From: Jacob Chen <jacob2.chen@rock-chips.com>
> >>>
> >>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY
driver
> >>
> >> Should this really be a subdev driver ? After a quick look at the
code, the
> >> only parameters you need to configure the PHY is the number of lanes
and the
> >> data rate. Implementing the whole subdev API seems overcomplicated to
me,
> >> especially given that the D-PHY doesn't deal with video streams as
such, but
> >> operates one level down. Shouldn't we model the D-PHY using the Linux
PHY
> >> framework ? I believe all the features you need are there except for a
D-PHY-
> >> specific configuration function that should be very easy to add.
> >>
> >
> > It deserves a subdev driver since the ISP is not the only user.
> > Other driver, like VIP, use it too.
> >
> >
> For example, if there are two sensors connected to a rk3399 board.
> Sensor1 --> DPHY1
> Sensor2 --> DPHY2
> With a subdev phy driver, i can choose either ISP or VIP for
> sensor1/sensor2 by enable/disable media link in the run time.
> 1.
> Sensor1 --> DPHY1 ---> VIP
> Sensor2 --> DPHY2 ---> ISP1
> 2.
> Sensor1 --> DPHY1 ---> ISP1
> Sensor2 --> DPHY2 ---> VIP
What is VIP?
Also, if we model the DPHY using the PHY interface, it will be still
possible to achieve the same, just by toggling the link between sensor and
VIP or ISP1:
1.
Sensor1 -------|~|--- VIP
\ | (PHY interface)
\ DPHY1
\ | (PHY interface)
\---| |-- ISP1
Sensor2 -------| |-- VIP
\ | (PHY interface)
\ DPHY2
\ | (PHY interface)
\---|~|-- ISP1
2.
Sensor1 -------| |-- VIP
\ | (PHY interface)
\ DPHY1
\ | (PHY interface)
\---|~|-- ISP1
Sensor2 -------|~|-- VIP
\ | (PHY interface)
\ DPHY2
\ | (PHY interface)
\---| |-- ISP1
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:41 ` Tomasz Figa
@ 2018-05-24 11:30 ` Baruch Siach
2018-05-25 4:35 ` Tomasz Figa
0 siblings, 1 reply; 50+ messages in thread
From: Baruch Siach @ 2018-05-24 11:30 UTC (permalink / raw)
To: linux-arm-kernel
Hi Tomasz,
On Mon, May 07, 2018 at 06:41:50AM +0000, Tomasz Figa wrote:
> On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > > > +{
> > > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > > + int ret;
> > > > > +
> > > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> > > on);
> > > > > +
> > > > > + if (on) {
> > > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > > + if (ret < 0)
> > > > > + return ret;
> > > > > +
> > > > > + rkisp1_config_clk(isp_dev);
> > > > > + } else {
> > > > > + ret = pm_runtime_put(isp_dev->dev);
> > >
> > > > I commented this line out to make more than one STREAMON work.
> Otherwise,
> > > > the second STREAMON hangs. I guess the bug is not this driver.
> Probably
> > > > something in drivers/soc/rockchip/pm_domains.c. Just noting that in
> case
> > > > you or someone on Cc would like to investigate it further.
> > > >
> > > > I tested v4.16-rc4 on the Tinkerboard.
> > >
> > > Looks like that version doesn't include the IOMMU PM and clock handling
> > > rework [1], which should fix a lot of runtime PM issues. FWIW,
> linux-next
> > > seems to already include it.
> > >
> > > [1] https://lkml.org/lkml/2018/3/23/44
>
> > Thanks for the reference.
>
> > It looks like the iommu driver part is in Linus' tree already. The DT
> part is
> > in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
>
> You're right, most of the series made it in time for 4.17. However, the DT
> part (precisely, the clocks properties added to IOMMU nodes) is crucial for
> the fixes to be effective.
>
> > Anyway, I'll take a look.
>
> Thanks for testing. :) (Forgot to mention in my previous email...)
I finally got around to testing. Unfortunately, kernel v4.17-rc6 with
cherry-pick of commit c78751f91c0b (ARM: dts: rockchip: add clocks in iommu
nodes) from Heiko's tree still exhibit the same problem. STREAMON hangs on
second try. The same workaround "fixes" it.
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-24 11:30 ` Baruch Siach
@ 2018-05-25 4:35 ` Tomasz Figa
0 siblings, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2018-05-25 4:35 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, May 24, 2018 at 8:30 PM Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Tomasz,
> On Mon, May 07, 2018 at 06:41:50AM +0000, Tomasz Figa wrote:
> > On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > > On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > > > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il>
wrote:
> > > > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int
on)
> > > > > > +{
> > > > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > > > + int ret;
> > > > > > +
> > > > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power:
%d\n",
> > > > on);
> > > > > > +
> > > > > > + if (on) {
> > > > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > > > + if (ret < 0)
> > > > > > + return ret;
> > > > > > +
> > > > > > + rkisp1_config_clk(isp_dev);
> > > > > > + } else {
> > > > > > + ret = pm_runtime_put(isp_dev->dev);
> > > >
> > > > > I commented this line out to make more than one STREAMON work.
> > Otherwise,
> > > > > the second STREAMON hangs. I guess the bug is not this driver.
> > Probably
> > > > > something in drivers/soc/rockchip/pm_domains.c. Just noting that
in
> > case
> > > > > you or someone on Cc would like to investigate it further.
> > > > >
> > > > > I tested v4.16-rc4 on the Tinkerboard.
> > > >
> > > > Looks like that version doesn't include the IOMMU PM and clock
handling
> > > > rework [1], which should fix a lot of runtime PM issues. FWIW,
> > linux-next
> > > > seems to already include it.
> > > >
> > > > [1] https://lkml.org/lkml/2018/3/23/44
> >
> > > Thanks for the reference.
> >
> > > It looks like the iommu driver part is in Linus' tree already. The DT
> > part is
> > > in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing
anything?
> >
> > You're right, most of the series made it in time for 4.17. However, the
DT
> > part (precisely, the clocks properties added to IOMMU nodes) is crucial
for
> > the fixes to be effective.
> >
> > > Anyway, I'll take a look.
> >
> > Thanks for testing. :) (Forgot to mention in my previous email...)
> I finally got around to testing. Unfortunately, kernel v4.17-rc6 with
> cherry-pick of commit c78751f91c0b (ARM: dts: rockchip: add clocks in
iommu
> nodes) from Heiko's tree still exhibit the same problem. STREAMON hangs on
> second try. The same workaround "fixes" it.
Thanks for testing. I'm out of ideas, since the same code seems to work
fine for us in Chrome OS 4.4 kernel. Maybe we could have someone from RK
take a look.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
` (19 preceding siblings ...)
2018-03-09 14:12 ` [PATCH v6 00/17] Rockchip ISP1 Driver Hans Verkuil
@ 2018-09-03 13:07 ` Hans Verkuil
2018-09-03 13:50 ` Tomasz Figa
20 siblings, 1 reply; 50+ messages in thread
From: Hans Verkuil @ 2018-09-03 13:07 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jacob,
What is the status of this patch series? I haven't seen any updates for quite
some time now, and I thought that it was getting close.
It would be nice to see this go in.
Regards,
Hans
On 03/08/2018 10:47 AM, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> changes in V6:
> - add mipi txrx phy support
> - remove bool and enum from uapi header
> - add buf_prepare op
> - correct some spelling problems
> - return all queued buffers when starting stream failed
>
> changes in V5: Sync with local changes,
> - fix the SP height limit
> - speed up the second stream capture
> - the second stream can't force sync for rsz when start/stop streaming
> - add frame id to param vb2 buf
> - enable luminance maximum threshold
>
> changes in V4:
> - fix some bugs during development
> - move quantization settings to rkisp1 subdev
> - correct some spelling problems
> - describe ports in dt-binding documents
>
> changes in V3:
> - add some comments
> - fix wrong use of v4l2_async_subdev_notifier_register
> - optimize two paths capture at a time
> - remove compose
> - re-struct headers
> - add a tmp wiki page: http://opensource.rock-chips.com/wiki_Rockchip-isp1
>
> changes in V2:
> mipi-phy:
> - use async probing
> - make it be a child device of the GRF
> isp:
> - add dummy buffer
> - change the way to get bus configuration, which make it possible to
> add parallel sensor support in the future(without mipi-phy driver).
>
> This patch series add a ISP(Camera) v4l2 driver for rockchip rk3288/rk3399 SoC.
>
> Wiki Pages:
> http://opensource.rock-chips.com/wiki_Rockchip-isp1
>
> The deprecated g_mbus_config op is not dropped in V6 because i am waiting tomasz's patches.
>
> v4l2-compliance for V6(isp params/stats nodes are passed):
>
> v4l2-compliance SHA : 93dc5f20727fede5097d67f8b9adabe4b8046d5b
>
> Compliance test for device /dev/video0:
>
> Driver Info:
> Driver name : rkisp1
> Card type : rkisp1
> Bus info : platform:ff910000.isp
> Driver version : 4.16.0
> Capabilities : 0x84201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
> Device Capabilities
> Device Caps : 0x04201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
> Media Driver Info:
> Driver name : rkisp1
> Model : rkisp1
> Serial :
> Bus info :
> Media version : 4.16.0
> Hardware revision: 0x00000000 (0)
> Driver version : 4.16.0
> Interface Info:
> ID : 0x03000007
> Type : V4L Video
> Entity Info:
> ID : 0x00000006 (6)
> Name : rkisp1_selfpath
> Function : V4L2 I/O
> Pad 0x01000009 : Sink
> Link 0x02000021: from remote pad 0x1000004 of entity 'rkisp1-isp-subdev': Data, Enabled
>
> Required ioctls:
> test MC information (see 'Media Driver Info' above): OK
> test VIDIOC_QUERYCAP: OK
>
> Allow for multiple opens:
> test second /dev/video0 open: OK
> test VIDIOC_QUERYCAP: OK
> test VIDIOC_G/S_PRIORITY: OK
> test for unlimited opens: OK
>
> Debug ioctls:
> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> test VIDIOC_LOG_STATUS: OK (Not Supported)
>
> Input ioctls:
> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> test VIDIOC_ENUMAUDIO: OK (Not Supported)
> test VIDIOC_G/S/ENUMINPUT: OK
> test VIDIOC_G/S_AUDIO: OK (Not Supported)
> Inputs: 1 Audio Inputs: 0 Tuners: 0
>
> Output ioctls:
> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> Outputs: 0 Audio Outputs: 0 Modulators: 0
>
> Input/Output configuration ioctls:
> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> test VIDIOC_G/S_EDID: OK (Not Supported)
>
> Control ioctls (Input 0):
> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> test VIDIOC_QUERYCTRL: OK
> test VIDIOC_G/S_CTRL: OK
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> Standard Controls: 9 Private Controls: 0
>
> Format ioctls (Input 0):
> test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> test VIDIOC_G/S_PARM: OK (Not Supported)
> test VIDIOC_G_FBUF: OK (Not Supported)
> fail: v4l2-test-formats.cpp(330): !colorspace
> fail: v4l2-test-formats.cpp(454): testColorspace(pix_mp.pixelformat, pix_mp.colorspace, pix_mp.ycbcr_enc, pix_m
> p.quantization)
> test VIDIOC_G_FMT: FAIL
> test VIDIOC_TRY_FMT: OK (Not Supported)
> test VIDIOC_S_FMT: OK (Not Supported)
> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> fail: v4l2-test-formats.cpp(1288): doioctl(node, VIDIOC_G_SELECTION, &sel) != EINVAL
> test Cropping: FAIL
> test Composing: OK (Not Supported)
> test Scaling: OK
>
> Codec ioctls (Input 0):
> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>
> Buffer ioctls (Input 0):
> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> fail: v4l2-test-buffers.cpp(525): VIDIOC_EXPBUF is supported, but the V4L2_MEMORY_MMAP support is missing, prob
> ably due to earlier failing format tests.
> test VIDIOC_EXPBUF: OK (Not Supported)
>
> Total: 44, Succeeded: 42, Failed: 2, Warnings: 0
>
> Jacob Chen (12):
> media: doc: add document for rkisp1 meta buffer format
> media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
> media: rkisp1: add Rockchip ISP1 subdev driver
> media: rkisp1: add ISP1 statistics driver
> media: rkisp1: add ISP1 params driver
> media: rkisp1: add capture device driver
> media: rkisp1: add rockchip isp1 core driver
> dt-bindings: Document the Rockchip ISP1 bindings
> dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
> ARM: dts: rockchip: add isp node for rk3288
> ARM: dts: rockchip: add rx0 mipi-phy for rk3288
> MAINTAINERS: add entry for Rockchip ISP1 driver
>
> Jeffy Chen (1):
> media: rkisp1: Add user space ABI definitions
>
> Shunqian Zheng (3):
> media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
> arm64: dts: rockchip: add isp0 node for rk3399
> arm64: dts: rockchip: add rx0 mipi-phy for rk3399
>
> Wen Nuan (1):
> ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node for rk3288
>
> .../devicetree/bindings/media/rockchip-isp1.txt | 69 +
> .../bindings/media/rockchip-mipi-dphy.txt | 90 +
> Documentation/media/uapi/v4l/meta-formats.rst | 2 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-params.rst | 20 +
> .../media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst | 18 +
> MAINTAINERS | 10 +
> arch/arm/boot/dts/rk3288.dtsi | 33 +
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 25 +
> drivers/media/platform/Kconfig | 10 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/rockchip/isp1/Makefile | 8 +
> drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/capture.h | 167 ++
> drivers/media/platform/rockchip/isp1/common.h | 110 ++
> drivers/media/platform/rockchip/isp1/dev.c | 626 +++++++
> drivers/media/platform/rockchip/isp1/dev.h | 93 ++
> drivers/media/platform/rockchip/isp1/isp_params.c | 1539 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/isp_params.h | 49 +
> drivers/media/platform/rockchip/isp1/isp_stats.c | 508 ++++++
> drivers/media/platform/rockchip/isp1/isp_stats.h | 58 +
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> drivers/media/platform/rockchip/isp1/regs.c | 239 +++
> drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 ++
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
> include/uapi/linux/rkisp1-config.h | 798 +++++++++
> include/uapi/linux/videodev2.h | 4 +
> 29 files changed, 9945 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-params.rst
> create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-rkisp1-stat.rst
> create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
> create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
> create mode 100644 drivers/media/platform/rockchip/isp1/common.h
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
> create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
> create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
> create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
> create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
> create mode 100644 include/uapi/linux/rkisp1-config.h
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-09-03 13:07 ` Hans Verkuil
@ 2018-09-03 13:50 ` Tomasz Figa
2018-09-03 14:15 ` Heiko Stuebner
0 siblings, 1 reply; 50+ messages in thread
From: Tomasz Figa @ 2018-09-03 13:50 UTC (permalink / raw)
To: linux-arm-kernel
Hi Hans,
On Mon, Sep 3, 2018 at 10:07 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
>
> Hi Jacob,
>
> What is the status of this patch series? I haven't seen any updates for quite
> some time now, and I thought that it was getting close.
>
> It would be nice to see this go in.
AFAIK, Jacob has left the project. +william.hu at rock-chips.com , who
has been working on addressing the remaining comments for a while.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v6 00/17] Rockchip ISP1 Driver
2018-09-03 13:50 ` Tomasz Figa
@ 2018-09-03 14:15 ` Heiko Stuebner
0 siblings, 0 replies; 50+ messages in thread
From: Heiko Stuebner @ 2018-09-03 14:15 UTC (permalink / raw)
To: linux-arm-kernel
Am Montag, 3. September 2018, 15:50:20 CEST schrieb Tomasz Figa:
> Hi Hans,
>
> On Mon, Sep 3, 2018 at 10:07 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
> >
> > Hi Jacob,
> >
> > What is the status of this patch series? I haven't seen any updates for quite
> > some time now, and I thought that it was getting close.
> >
> > It would be nice to see this go in.
>
> AFAIK, Jacob has left the project. +william.hu at rock-chips.com , who
> has been working on addressing the remaining comments for a while.
also I think Ezequiel has spent time on the vpu driver.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2018-05-16 5:20 ` Laurent Pinchart
@ 2019-03-10 17:49 ` Laurent Pinchart
2019-03-11 9:37 ` Tomasz Figa
1 sibling, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2019-03-10 17:49 UTC (permalink / raw)
To: Jacob Chen
Cc: devicetree, eddie.cai.linux, heiko, Jacob Chen, jeffy.chen, zyc,
linux-kernel, tfiga, linux-rockchip, hans.verkuil, sakari.ailus,
mchehab, zhengsq, linux-arm-kernel, linux-media
Hi Jacob,
A few more comments on the code this time.
First of all, this has bit-rotten a bit and doesn't compile. The
following patch fixes it. Feel free to squash it into this patch (no
need to credit me or add my SoB line).
commit 297399bb5e3ac8d50f27f1c911fe7e5f26983e56
Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Sat Mar 2 02:18:19 2019 +0200
media: rkisp1: Fix compilation errors
The code has bit-rotten since March 2018, fix compilation errors.
The new V4L2 async notifier API requires notifiers to be initialized by
a call to v4l2_async_notifier_init() before being used, do so.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
index 32140960557a..8a7d070314c9 100644
--- a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
@@ -697,7 +697,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
container_of(asd, struct sensor_async_subdev, asd);
struct v4l2_mbus_config *config = &s_asd->mbus;
- if (vep->bus_type != V4L2_MBUS_CSI2) {
+ if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
dev_err(dev, "Only CSI2 bus type is currently supported\n");
return -EINVAL;
}
@@ -707,7 +707,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
return -EINVAL;
}
- config->type = V4L2_MBUS_CSI2;
+ config->type = V4L2_MBUS_CSI2_DPHY;
config->flags = vep->bus.mipi_csi2.flags;
s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
@@ -745,6 +745,8 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
if (ret < 0)
return ret;
+ v4l2_async_notifier_init(&priv->notifier);
+
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
priv->dev, &priv->notifier,
sizeof(struct sensor_async_subdev), 0,
@@ -752,7 +754,7 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
if (ret < 0)
return ret;
- if (!priv->notifier.num_subdevs)
+ if (list_empty(&priv->notifier.asd_list))
return -ENODEV; /* no endpoint */
priv->sd.subdev_notifier = &priv->notifier;
Then, please see below for additional comments.
On Thu, Mar 08, 2018 at 05:47:54PM +0800, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> 2 files changed, 883 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> new file mode 100644
> index 000000000000..32140960557a
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -0,0 +1,868 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define RK3288_GRF_SOC_CON6 0x025c
> +#define RK3288_GRF_SOC_CON8 0x0264
> +#define RK3288_GRF_SOC_CON9 0x0268
> +#define RK3288_GRF_SOC_CON10 0x026c
> +#define RK3288_GRF_SOC_CON14 0x027c
> +#define RK3288_GRF_SOC_STATUS21 0x02d4
> +#define RK3288_GRF_IO_VSEL 0x0380
> +#define RK3288_GRF_SOC_CON15 0x03a4
> +
> +#define RK3399_GRF_SOC_CON9 0x6224
> +#define RK3399_GRF_SOC_CON21 0x6254
> +#define RK3399_GRF_SOC_CON22 0x6258
> +#define RK3399_GRF_SOC_CON23 0x625c
> +#define RK3399_GRF_SOC_CON24 0x6260
> +#define RK3399_GRF_SOC_CON25 0x6264
> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> +
> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> +#define LANE0_HS_RX_CONTROL 0x44
> +#define LANE1_HS_RX_CONTROL 0x54
> +#define LANE2_HS_RX_CONTROL 0x84
> +#define LANE3_HS_RX_CONTROL 0x94
> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> +
> +/*
> + * CSI HOST
> + */
> +#define CSIHOST_PHY_TEST_CTRL0 0x30
> +#define CSIHOST_PHY_TEST_CTRL1 0x34
> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> +#define CSIHOST_DPHY_RSTZ 0x0c
> +
> +#define PHY_TESTEN_ADDR (0x1 << 16)
> +#define PHY_TESTEN_DATA (0x0 << 16)
> +#define PHY_TESTCLK (0x1 << 1)
> +#define PHY_TESTCLR (0x1 << 0)
> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> +
> +#define HIWORD_UPDATE(val, mask, shift) \
> + ((val) << (shift) | (mask) << ((shift) + 16))
> +
> +enum mipi_dphy_sy_pads {
> + MIPI_DPHY_SY_PAD_SINK = 0,
> + MIPI_DPHY_SY_PAD_SOURCE,
> + MIPI_DPHY_SY_PADS_NUM,
> +};
> +
> +enum dphy_reg_id {
> + GRF_DPHY_RX0_TURNDISABLE = 0,
> + GRF_DPHY_RX0_FORCERXMODE,
> + GRF_DPHY_RX0_FORCETXSTOPMODE,
> + GRF_DPHY_RX0_ENABLE,
> + GRF_DPHY_RX0_TESTCLR,
> + GRF_DPHY_RX0_TESTCLK,
> + GRF_DPHY_RX0_TESTEN,
> + GRF_DPHY_RX0_TESTDIN,
> + GRF_DPHY_RX0_TURNREQUEST,
> + GRF_DPHY_RX0_TESTDOUT,
> + GRF_DPHY_TX0_TURNDISABLE,
> + GRF_DPHY_TX0_FORCERXMODE,
> + GRF_DPHY_TX0_FORCETXSTOPMODE,
> + GRF_DPHY_TX0_TURNREQUEST,
> + GRF_DPHY_TX1RX1_TURNDISABLE,
> + GRF_DPHY_TX1RX1_FORCERXMODE,
> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> + GRF_DPHY_TX1RX1_ENABLE,
> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> + GRF_DPHY_TX1RX1_BASEDIR,
> + GRF_DPHY_TX1RX1_ENABLECLK,
> + GRF_DPHY_TX1RX1_TURNREQUEST,
> + GRF_DPHY_RX1_SRC_SEL,
> + /* rk3288 only */
> + GRF_CON_DISABLE_ISP,
> + GRF_CON_ISP_DPHY_SEL,
> + GRF_DSI_CSI_TESTBUS_SEL,
> + GRF_DVP_V18SEL,
> + /* below is for rk3399 only */
> + GRF_DPHY_RX0_CLK_INV_SEL,
> + GRF_DPHY_RX1_CLK_INV_SEL,
> +};
> +
> +struct dphy_reg {
> + u32 offset;
> + u32 mask;
> + u32 shift;
> +};
> +
> +#define PHY_REG(_offset, _width, _shift) \
> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> +
> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> +};
> +
> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> +};
> +
> +struct hsfreq_range {
> + u32 range_h;
> + u8 cfg_bit;
> +};
> +
> +struct mipidphy_priv;
> +
> +struct dphy_drv_data {
> + const char * const *clks;
> + int num_clks;
> + const struct hsfreq_range *hsfreq_ranges;
> + int num_hsfreq_ranges;
> + const struct dphy_reg *regs;
> +};
> +
> +struct sensor_async_subdev {
> + struct v4l2_async_subdev asd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +#define MAX_DPHY_CLK 8
> +#define MAX_DPHY_SENSORS 2
> +
> +struct mipidphy_sensor {
> + struct v4l2_subdev *sd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +struct mipidphy_priv {
> + struct device *dev;
> + struct regmap *regmap_grf;
> + const struct dphy_reg *grf_regs;
> + struct clk *clks[MAX_DPHY_CLK];
> + const struct dphy_drv_data *drv_data;
> + u64 data_rate_mbps;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
Should we really hardcode the maximum number of sensors ? Wouldn't it be
better to allocate this dynamically ?
> + int num_sensors;
> + bool is_streaming;
> + void __iomem *txrx_base_addr;
> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> +};
> +
> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
> +{
> + return container_of(subdev, struct mipidphy_priv, sd);
> +}
> +
> +static inline void write_grf_reg(struct mipidphy_priv *priv,
> + int index, u8 value)
> +{
> + const struct dphy_reg *reg = &priv->grf_regs[index];
> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> +
> + WARN_ON(!reg->offset);
> + regmap_write(priv->regmap_grf, reg->offset, val);
> +}
> +
> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> + u8 test_code, u8 test_data)
> +{
> + /*
> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> + * is latched internally as the current test code. Test data is
> + * programmed internally by rising edge on TESTCLK.
> + */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> +}
> +
> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
> + unsigned char data)
> +{
> + /*
> + * TESTEN =1,TESTDIN=addr
> + * TESTCLK=0
> + * TESTEN =0,TESTDIN=data
> + * TESTCLK=1
> + */
> + writel((PHY_TESTEN_ADDR | addr),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel((PHY_TESTEN_DATA | data),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> +}
> +
> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> +{
> + struct media_pad *local, *remote;
> + struct media_entity *sensor_me;
> +
> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> + remote = media_entity_remote_pad(local);
> + if (!remote) {
> + v4l2_warn(sd, "No link between dphy and sensor\n");
> + return NULL;
> + }
> +
> + sensor_me = media_entity_remote_pad(local)->entity;
> + return media_entity_to_v4l2_subdev(sensor_me);
You could call this at the beginning of mipidphy_s_stream_start() and
pass the sensor pointer to mipidphy_get_sensor_data_rate() and the
.stream_on() operations to avoid multiple costly lookups, or possibly
cache it in the mipidphy_priv structure (in that case I'd reset it to
NULL at stream off time).
> +}
> +
> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + int i;
> +
> + for (i = 0; i < priv->num_sensors; ++i)
> + if (priv->sensors[i].sd == sd)
> + return &priv->sensors[i];
> +
> + return NULL;
> +}
> +
> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> + int ret;
> +
> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
The correct control for this is V4L2_CID_PIXEL_RATE. You will have to
divide it by the number of lanes to get the data rate per lane, but
there will be no need to multiply it by 2 as below.
> + if (!link_freq) {
> + v4l2_warn(sd, "No pixel rate control in subdev\n");
> + return -EPIPE;
> + }
> +
> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> + if (ret < 0) {
> + v4l2_err(sd, "Failed to get menu item\n");
> + return ret;
> + }
> +
> + if (!qm.value) {
> + v4l2_err(sd, "Invalid link_freq\n");
> + return -EINVAL;
> + }
> + priv->data_rate_mbps = qm.value * 2;
> + do_div(priv->data_rate_mbps, 1000 * 1000);
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int ret = 0;
> +
> + if (priv->is_streaming)
> + return 0;
> +
> + ret = mipidphy_get_sensor_data_rate(sd);
> + if (ret < 0)
> + return ret;
> +
> + priv->stream_on(priv, sd);
> +
> + priv->is_streaming = true;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + priv->is_streaming = false;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> +{
> + if (on)
> + return mipidphy_s_stream_start(sd);
> + else
> + return mipidphy_s_stream_stop(sd);
> +}
> +
> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> + struct v4l2_mbus_config *config)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> +
> + *config = sensor->mbus;
> +
> + return 0;
> +}
This seems like a hack :-(
> +
> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + if (on)
> + return pm_runtime_get_sync(priv->dev);
> + else
> + return pm_runtime_put(priv->dev);
> +}
> +
> +static int mipidphy_runtime_suspend(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = num_clks - 1; i >= 0; i--)
> + clk_disable_unprepare(priv->clks[i]);
> +
> + return 0;
> +}
> +
> +static int mipidphy_runtime_resume(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks, ret;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = 0; i < num_clks; i++) {
> + ret = clk_prepare_enable(priv->clks[i]);
> + if (ret < 0)
> + goto err;
> + }
> +
> + return 0;
> +err:
> + while (--i >= 0)
> + clk_disable_unprepare(priv->clks[i]);
> + return ret;
> +}
> +
> +/* dphy accepts all fmt/size from sensor */
> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> +
> + /*
> + * Do not allow format changes and just relay whatever
> + * set currently in the sensor.
> + */
> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
It's userspace responsibility to propagate formats through the pipeline
when using the MC API, you shouldn't access the format of the source
subdev in this driver.
> +}
> +
> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> + .set_fmt = mipidphy_get_set_fmt,
> + .get_fmt = mipidphy_get_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> + .s_power = mipidphy_s_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> + .g_mbus_config = mipidphy_g_mbus_config,
> + .s_stream = mipidphy_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> + .core = &mipidphy_core_ops,
> + .video = &mipidphy_video_ops,
> + .pad = &mipidphy_subdev_pad_ops,
> +};
> +
> +/* These tables must be sorted by .range_h ascending. */
> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}
> +};
> +
> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> +};
> +
> +static const char * const rk3399_mipidphy_clks[] = {
> + "dphy-ref",
> + "dphy-cfg",
> + "grf",
> +};
> +
> +static const char * const rk3288_mipidphy_clks[] = {
> + "dphy-ref",
> + "pclk",
> +};
> +
> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> +
> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> +
> + /* dphy start */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> + usleep_range(100, 150);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + /* HS hsfreq_range & lane 0 settle bypass */
> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane0 */
> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + /* HS RX Control of lane1 */
> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane2 */
> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane3 */
> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy0_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> + GENMASK(sensor->lanes - 1, 0));
> + /* dphy start */
> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy1_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> + .clks = rk3288_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> + .regs = rk3288_grf_dphy_regs,
> +};
> +
> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> + .clks = rk3399_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> + .regs = rk3399_grf_dphy_regs,
> +};
> +
> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> + {
> + .compatible = "rockchip,rk3399-mipi-dphy",
> + .data = &rk3399_mipidphy_drv_data,
> + },
> + {
> + .compatible = "rockchip,rk3288-mipi-dphy",
> + .data = &rk3288_mipidphy_drv_data,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
You can mode this just above the probe function to group it with the
code that uses it.
> +
> +/* The .bound() notifier callback when a match is found */
> +static int
> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct sensor_async_subdev *s_asd = container_of(asd,
> + struct sensor_async_subdev, asd);
> + struct mipidphy_sensor *sensor;
> + unsigned int pad, ret;
> +
> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> + return -EBUSY;
> +
> + sensor = &priv->sensors[priv->num_sensors++];
> + sensor->lanes = s_asd->lanes;
> + sensor->mbus = s_asd->mbus;
> + sensor->sd = sd;
> +
> + for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
> + if (sensor->sd->entity.pads[pad].flags
> + & MEDIA_PAD_FL_SOURCE)
> + break;
> +
> + if (pad == sensor->sd->entity.num_pads) {
> + dev_err(priv->dev,
> + "failed to find src pad for %s\n",
> + sensor->sd->name);
> +
> + return -ENXIO;
> + }
> +
> + ret = media_create_pad_link(
> + &sensor->sd->entity, pad,
> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to create link for %s\n",
> + sensor->sd->name);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* The .unbind callback */
> +static void
> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> +
> + sensor->sd = NULL;
> +}
> +
> +static const struct
> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> + .bound = rockchip_mipidphy_notifier_bound,
> + .unbind = rockchip_mipidphy_notifier_unbind,
> +};
> +
> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> + struct v4l2_fwnode_endpoint *vep,
> + struct v4l2_async_subdev *asd)
> +{
> + struct sensor_async_subdev *s_asd =
> + container_of(asd, struct sensor_async_subdev, asd);
> + struct v4l2_mbus_config *config = &s_asd->mbus;
> +
> + if (vep->bus_type != V4L2_MBUS_CSI2) {
> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> + return -EINVAL;
> + }
> +
> + if (vep->base.port != 0) {
> + dev_err(dev, "The PHY has only port 0\n");
> + return -EINVAL;
> + }
> +
> + config->type = V4L2_MBUS_CSI2;
> + config->flags = vep->bus.mipi_csi2.flags;
> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> +
> + switch (vep->bus.mipi_csi2.num_data_lanes) {
> + case 1:
> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> + break;
> + case 2:
> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> + break;
> + case 3:
> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> + break;
> + case 4:
> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> +{
> + int ret;
> +
> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> +
> + ret = media_entity_pads_init(&priv->sd.entity,
> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> + if (ret < 0)
> + return ret;
> +
> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> + priv->dev, &priv->notifier,
> + sizeof(struct sensor_async_subdev), 0,
> + rockchip_mipidphy_fwnode_parse);
> + if (ret < 0)
> + return ret;
> +
> + if (!priv->notifier.num_subdevs)
> + return -ENODEV; /* no endpoint */
> +
> + priv->sd.subdev_notifier = &priv->notifier;
> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to register async notifier : %d\n", ret);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + return v4l2_async_register_subdev(&priv->sd);
> +}
> +
> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct v4l2_subdev *sd;
> + struct mipidphy_priv *priv;
> + struct regmap *grf;
> + struct resource *res;
> + const struct of_device_id *of_id;
> + const struct dphy_drv_data *drv_data;
> + int i, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->dev = dev;
> +
> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + grf = syscon_node_to_regmap(dev->parent->of_node);
> + if (IS_ERR(grf)) {
> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(grf)) {
> + dev_err(dev, "Can't find GRF syscon\n");
> + return -ENODEV;
> + }
> + }
> + priv->regmap_grf = grf;
> +
> + drv_data = of_id->data;
> + for (i = 0; i < drv_data->num_clks; i++) {
> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> +
> + if (IS_ERR(priv->clks[i])) {
> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> + return PTR_ERR(priv->clks[i]);
> + }
> + }
> +
> + priv->grf_regs = drv_data->regs;
> + priv->drv_data = drv_data;
> + priv->stream_on = mipidphy_txrx_stream_on;
> + priv->txrx_base_addr = NULL;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
This will result in an error being printed to the kernel log if res is
NULL. The following (untested) code should fix it.
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
priv->txrx_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->txrx_base_addr))
return PTR_ERR(priv->txrx_base_addr);
priv->stream_on = mipidphy_txrx_stream_on;
} else {
priv->txrx_base_addr = NULL;
priv->stream_on = mipidphy_rx_stream_on;
}
Furthermore, txrx_base_addr seems to be used to access the CSIHOST
registers, part of the CSI-2 receiver (CSI host in the system connection
description of the RK3288 datasheet, in the MIPI CSI PHY section), not
the PHY itself. I'm afraid you'll have to redesign this to split the
code between those two components. One option would be to have a CSI
host DT node with a reg resource for the CSIHOST registers, modeled as a
V4L2 subdev, and pointing to the DPHY using a phy-handle property. In
the rkisp1 driver, I would then extract the CSI-2 receiver code to a
separate subdev, and handle the PHY from that subdev, also with a
phy-handle property in the rkisp1 DT node.
> + if (IS_ERR(priv->txrx_base_addr))
> + priv->stream_on = mipidphy_rx_stream_on;
> +
> + sd = &priv->sd;
> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> + sd->dev = dev;
> +
> + platform_set_drvdata(pdev, &sd->entity);
> +
> + ret = rockchip_mipidphy_media_init(priv);
> + if (ret < 0)
> + return ret;
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> +{
> + struct media_entity *me = platform_get_drvdata(pdev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> +
> + media_entity_cleanup(&sd->entity);
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> + mipidphy_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver rockchip_isp_mipidphy_driver = {
> + .probe = rockchip_mipidphy_probe,
> + .remove = rockchip_mipidphy_remove,
> + .driver = {
> + .name = "rockchip-sy-mipi-dphy",
> + .pm = &rockchip_mipidphy_pm_ops,
> + .of_match_table = rockchip_mipidphy_match_id,
Too much indentation.
> + },
> +};
> +
> +module_platform_driver(rockchip_isp_mipidphy_driver);
dev.c also has a module_platform_driver(). As both are compiled in the
same module, this results in a link error when compiling the drivers as
a module. I would recommend separating it into two modules to fix this.
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> new file mode 100644
> index 000000000000..c558791064a2
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef __MIPI_DPHY_SY_H__
> +#define __MIPI_DPHY_SY_H__
> +
> +#include <media/v4l2-subdev.h>
> +
> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
This function doesn't seem to be used or defined anywhere, you can drop
this file.
> +
> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
Regards,
Laurent Pinchart
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 50+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2019-03-10 17:49 ` Laurent Pinchart
@ 2019-03-11 9:37 ` Tomasz Figa
0 siblings, 0 replies; 50+ messages in thread
From: Tomasz Figa @ 2019-03-11 9:37 UTC (permalink / raw)
To: Laurent Pinchart
Cc: devicetree, Eddie Cai, Heiko Stübner, Jeffy,
钟以崇, Linux Kernel Mailing List,
胡克俊, open list:ARM/Rockchip SoC...,
Hans Verkuil, Sakari Ailus, Mauro Carvalho Chehab, Shunqian Zheng,
list@263.net:IOMMU DRIVERS <iommu@lists.linux-foundation.org>, Joerg Roedel <joro@8bytes.org>, ,
Linux Media Mailing List
Hi Laurent,
On Mon, Mar 11, 2019 at 2:49 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Jacob,
>
> A few more comments on the code this time.
>
> First of all, this has bit-rotten a bit and doesn't compile. The
> following patch fixes it. Feel free to squash it into this patch (no
> need to credit me or add my SoB line).
Thanks a lot.
-Jacob, who's not working on this driver anymore.
+胡克俊 (William), who's been looking into reviving this series.
>
> commit 297399bb5e3ac8d50f27f1c911fe7e5f26983e56
> Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Date: Sat Mar 2 02:18:19 2019 +0200
>
> media: rkisp1: Fix compilation errors
>
> The code has bit-rotten since March 2018, fix compilation errors.
>
> The new V4L2 async notifier API requires notifiers to be initialized by
> a call to v4l2_async_notifier_init() before being used, do so.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> index 32140960557a..8a7d070314c9 100644
> --- a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -697,7 +697,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> container_of(asd, struct sensor_async_subdev, asd);
> struct v4l2_mbus_config *config = &s_asd->mbus;
>
> - if (vep->bus_type != V4L2_MBUS_CSI2) {
> + if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
> dev_err(dev, "Only CSI2 bus type is currently supported\n");
> return -EINVAL;
> }
> @@ -707,7 +707,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> return -EINVAL;
> }
>
> - config->type = V4L2_MBUS_CSI2;
> + config->type = V4L2_MBUS_CSI2_DPHY;
> config->flags = vep->bus.mipi_csi2.flags;
> s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>
> @@ -745,6 +745,8 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> if (ret < 0)
> return ret;
>
> + v4l2_async_notifier_init(&priv->notifier);
> +
> ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> priv->dev, &priv->notifier,
> sizeof(struct sensor_async_subdev), 0,
> @@ -752,7 +754,7 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> if (ret < 0)
> return ret;
>
> - if (!priv->notifier.num_subdevs)
> + if (list_empty(&priv->notifier.asd_list))
> return -ENODEV; /* no endpoint */
>
> priv->sd.subdev_notifier = &priv->notifier;
>
>
> Then, please see below for additional comments.
>
> On Thu, Mar 08, 2018 at 05:47:54PM +0800, Jacob Chen wrote:
> > From: Jacob Chen <jacob2.chen@rock-chips.com>
> >
> > This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
> >
> > Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> > Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> > Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> > ---
> > .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
> > .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> > 2 files changed, 883 insertions(+)
> > create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> >
> > diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > new file mode 100644
> > index 000000000000..32140960557a
> > --- /dev/null
> > +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > @@ -0,0 +1,868 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Rockchip MIPI Synopsys DPHY driver
> > + *
> > + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#define RK3288_GRF_SOC_CON6 0x025c
> > +#define RK3288_GRF_SOC_CON8 0x0264
> > +#define RK3288_GRF_SOC_CON9 0x0268
> > +#define RK3288_GRF_SOC_CON10 0x026c
> > +#define RK3288_GRF_SOC_CON14 0x027c
> > +#define RK3288_GRF_SOC_STATUS21 0x02d4
> > +#define RK3288_GRF_IO_VSEL 0x0380
> > +#define RK3288_GRF_SOC_CON15 0x03a4
> > +
> > +#define RK3399_GRF_SOC_CON9 0x6224
> > +#define RK3399_GRF_SOC_CON21 0x6254
> > +#define RK3399_GRF_SOC_CON22 0x6258
> > +#define RK3399_GRF_SOC_CON23 0x625c
> > +#define RK3399_GRF_SOC_CON24 0x6260
> > +#define RK3399_GRF_SOC_CON25 0x6264
> > +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> > +
> > +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> > +#define LANE0_HS_RX_CONTROL 0x44
> > +#define LANE1_HS_RX_CONTROL 0x54
> > +#define LANE2_HS_RX_CONTROL 0x84
> > +#define LANE3_HS_RX_CONTROL 0x94
> > +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> > +
> > +/*
> > + * CSI HOST
> > + */
> > +#define CSIHOST_PHY_TEST_CTRL0 0x30
> > +#define CSIHOST_PHY_TEST_CTRL1 0x34
> > +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> > +#define CSIHOST_DPHY_RSTZ 0x0c
> > +
> > +#define PHY_TESTEN_ADDR (0x1 << 16)
> > +#define PHY_TESTEN_DATA (0x0 << 16)
> > +#define PHY_TESTCLK (0x1 << 1)
> > +#define PHY_TESTCLR (0x1 << 0)
> > +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> > +
> > +#define HIWORD_UPDATE(val, mask, shift) \
> > + ((val) << (shift) | (mask) << ((shift) + 16))
> > +
> > +enum mipi_dphy_sy_pads {
> > + MIPI_DPHY_SY_PAD_SINK = 0,
> > + MIPI_DPHY_SY_PAD_SOURCE,
> > + MIPI_DPHY_SY_PADS_NUM,
> > +};
> > +
> > +enum dphy_reg_id {
> > + GRF_DPHY_RX0_TURNDISABLE = 0,
> > + GRF_DPHY_RX0_FORCERXMODE,
> > + GRF_DPHY_RX0_FORCETXSTOPMODE,
> > + GRF_DPHY_RX0_ENABLE,
> > + GRF_DPHY_RX0_TESTCLR,
> > + GRF_DPHY_RX0_TESTCLK,
> > + GRF_DPHY_RX0_TESTEN,
> > + GRF_DPHY_RX0_TESTDIN,
> > + GRF_DPHY_RX0_TURNREQUEST,
> > + GRF_DPHY_RX0_TESTDOUT,
> > + GRF_DPHY_TX0_TURNDISABLE,
> > + GRF_DPHY_TX0_FORCERXMODE,
> > + GRF_DPHY_TX0_FORCETXSTOPMODE,
> > + GRF_DPHY_TX0_TURNREQUEST,
> > + GRF_DPHY_TX1RX1_TURNDISABLE,
> > + GRF_DPHY_TX1RX1_FORCERXMODE,
> > + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> > + GRF_DPHY_TX1RX1_ENABLE,
> > + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> > + GRF_DPHY_TX1RX1_BASEDIR,
> > + GRF_DPHY_TX1RX1_ENABLECLK,
> > + GRF_DPHY_TX1RX1_TURNREQUEST,
> > + GRF_DPHY_RX1_SRC_SEL,
> > + /* rk3288 only */
> > + GRF_CON_DISABLE_ISP,
> > + GRF_CON_ISP_DPHY_SEL,
> > + GRF_DSI_CSI_TESTBUS_SEL,
> > + GRF_DVP_V18SEL,
> > + /* below is for rk3399 only */
> > + GRF_DPHY_RX0_CLK_INV_SEL,
> > + GRF_DPHY_RX1_CLK_INV_SEL,
> > +};
> > +
> > +struct dphy_reg {
> > + u32 offset;
> > + u32 mask;
> > + u32 shift;
> > +};
> > +
> > +#define PHY_REG(_offset, _width, _shift) \
> > + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> > +
> > +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> > + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> > + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> > + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> > + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> > + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> > + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> > + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> > + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> > + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> > + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> > + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> > + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> > + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> > + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> > + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> > + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> > + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> > + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> > + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> > + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> > + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> > + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> > + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> > + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> > + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> > +};
> > +
> > +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> > + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> > + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> > + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> > + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> > + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> > + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> > + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> > + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> > + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> > + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> > + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> > + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> > + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> > + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> > + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> > + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> > + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> > + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> > + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> > + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> > + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> > + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> > + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> > + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> > + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> > + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> > + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> > +};
> > +
> > +struct hsfreq_range {
> > + u32 range_h;
> > + u8 cfg_bit;
> > +};
> > +
> > +struct mipidphy_priv;
> > +
> > +struct dphy_drv_data {
> > + const char * const *clks;
> > + int num_clks;
> > + const struct hsfreq_range *hsfreq_ranges;
> > + int num_hsfreq_ranges;
> > + const struct dphy_reg *regs;
> > +};
> > +
> > +struct sensor_async_subdev {
> > + struct v4l2_async_subdev asd;
> > + struct v4l2_mbus_config mbus;
> > + int lanes;
> > +};
> > +
> > +#define MAX_DPHY_CLK 8
> > +#define MAX_DPHY_SENSORS 2
> > +
> > +struct mipidphy_sensor {
> > + struct v4l2_subdev *sd;
> > + struct v4l2_mbus_config mbus;
> > + int lanes;
> > +};
> > +
> > +struct mipidphy_priv {
> > + struct device *dev;
> > + struct regmap *regmap_grf;
> > + const struct dphy_reg *grf_regs;
> > + struct clk *clks[MAX_DPHY_CLK];
> > + const struct dphy_drv_data *drv_data;
> > + u64 data_rate_mbps;
> > + struct v4l2_async_notifier notifier;
> > + struct v4l2_subdev sd;
> > + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> > + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>
> Should we really hardcode the maximum number of sensors ? Wouldn't it be
> better to allocate this dynamically ?
>
> > + int num_sensors;
> > + bool is_streaming;
> > + void __iomem *txrx_base_addr;
> > + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> > +};
> > +
> > +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct mipidphy_priv, sd);
> > +}
> > +
> > +static inline void write_grf_reg(struct mipidphy_priv *priv,
> > + int index, u8 value)
> > +{
> > + const struct dphy_reg *reg = &priv->grf_regs[index];
> > + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> > +
> > + WARN_ON(!reg->offset);
> > + regmap_write(priv->regmap_grf, reg->offset, val);
> > +}
> > +
> > +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> > + u8 test_code, u8 test_data)
> > +{
> > + /*
> > + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> > + * is latched internally as the current test code. Test data is
> > + * programmed internally by rising edge on TESTCLK.
> > + */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > +}
> > +
> > +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
> > + unsigned char data)
> > +{
> > + /*
> > + * TESTEN =1,TESTDIN=addr
> > + * TESTCLK=0
> > + * TESTEN =0,TESTDIN=data
> > + * TESTCLK=1
> > + */
> > + writel((PHY_TESTEN_ADDR | addr),
> > + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> > + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + writel((PHY_TESTEN_DATA | data),
> > + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > +}
> > +
> > +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> > +{
> > + struct media_pad *local, *remote;
> > + struct media_entity *sensor_me;
> > +
> > + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> > + remote = media_entity_remote_pad(local);
> > + if (!remote) {
> > + v4l2_warn(sd, "No link between dphy and sensor\n");
> > + return NULL;
> > + }
> > +
> > + sensor_me = media_entity_remote_pad(local)->entity;
> > + return media_entity_to_v4l2_subdev(sensor_me);
>
> You could call this at the beginning of mipidphy_s_stream_start() and
> pass the sensor pointer to mipidphy_get_sensor_data_rate() and the
> .stream_on() operations to avoid multiple costly lookups, or possibly
> cache it in the mipidphy_priv structure (in that case I'd reset it to
> NULL at stream off time).
>
> > +}
> > +
> > +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < priv->num_sensors; ++i)
> > + if (priv->sensors[i].sd == sd)
> > + return &priv->sensors[i];
> > +
> > + return NULL;
> > +}
> > +
> > +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct v4l2_ctrl *link_freq;
> > + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> > + int ret;
> > +
> > + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>
> The correct control for this is V4L2_CID_PIXEL_RATE. You will have to
> divide it by the number of lanes to get the data rate per lane, but
> there will be no need to multiply it by 2 as below.
>
> > + if (!link_freq) {
> > + v4l2_warn(sd, "No pixel rate control in subdev\n");
> > + return -EPIPE;
> > + }
> > +
> > + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> > + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> > + if (ret < 0) {
> > + v4l2_err(sd, "Failed to get menu item\n");
> > + return ret;
> > + }
> > +
> > + if (!qm.value) {
> > + v4l2_err(sd, "Invalid link_freq\n");
> > + return -EINVAL;
> > + }
> > + priv->data_rate_mbps = qm.value * 2;
> > + do_div(priv->data_rate_mbps, 1000 * 1000);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int ret = 0;
> > +
> > + if (priv->is_streaming)
> > + return 0;
> > +
> > + ret = mipidphy_get_sensor_data_rate(sd);
> > + if (ret < 0)
> > + return ret;
> > +
> > + priv->stream_on(priv, sd);
> > +
> > + priv->is_streaming = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > +
> > + priv->is_streaming = false;
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> > +{
> > + if (on)
> > + return mipidphy_s_stream_start(sd);
> > + else
> > + return mipidphy_s_stream_stop(sd);
> > +}
> > +
> > +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> > + struct v4l2_mbus_config *config)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > +
> > + *config = sensor->mbus;
> > +
> > + return 0;
> > +}
>
> This seems like a hack :-(
>
> > +
> > +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > +
> > + if (on)
> > + return pm_runtime_get_sync(priv->dev);
> > + else
> > + return pm_runtime_put(priv->dev);
> > +}
> > +
> > +static int mipidphy_runtime_suspend(struct device *dev)
> > +{
> > + struct media_entity *me = dev_get_drvdata(dev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int i, num_clks;
> > +
> > + num_clks = priv->drv_data->num_clks;
> > + for (i = num_clks - 1; i >= 0; i--)
> > + clk_disable_unprepare(priv->clks[i]);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_runtime_resume(struct device *dev)
> > +{
> > + struct media_entity *me = dev_get_drvdata(dev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int i, num_clks, ret;
> > +
> > + num_clks = priv->drv_data->num_clks;
> > + for (i = 0; i < num_clks; i++) {
> > + ret = clk_prepare_enable(priv->clks[i]);
> > + if (ret < 0)
> > + goto err;
> > + }
> > +
> > + return 0;
> > +err:
> > + while (--i >= 0)
> > + clk_disable_unprepare(priv->clks[i]);
> > + return ret;
> > +}
> > +
> > +/* dphy accepts all fmt/size from sensor */
> > +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> > +
> > + /*
> > + * Do not allow format changes and just relay whatever
> > + * set currently in the sensor.
> > + */
> > + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>
> It's userspace responsibility to propagate formats through the pipeline
> when using the MC API, you shouldn't access the format of the source
> subdev in this driver.
>
> > +}
> > +
> > +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> > + .set_fmt = mipidphy_get_set_fmt,
> > + .get_fmt = mipidphy_get_set_fmt,
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> > + .s_power = mipidphy_s_power,
> > +};
> > +
> > +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> > + .g_mbus_config = mipidphy_g_mbus_config,
> > + .s_stream = mipidphy_s_stream,
> > +};
> > +
> > +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> > + .core = &mipidphy_core_ops,
> > + .video = &mipidphy_video_ops,
> > + .pad = &mipidphy_subdev_pad_ops,
> > +};
> > +
> > +/* These tables must be sorted by .range_h ascending. */
> > +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> > + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> > + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> > + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> > + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> > + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> > + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> > + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> > + { 999, 0x1a}
> > +};
> > +
> > +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> > + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> > + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> > + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> > + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> > + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> > + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> > + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> > + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> > + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> > + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> > +};
> > +
> > +static const char * const rk3399_mipidphy_clks[] = {
> > + "dphy-ref",
> > + "dphy-cfg",
> > + "grf",
> > +};
> > +
> > +static const char * const rk3288_mipidphy_clks[] = {
> > + "dphy-ref",
> > + "pclk",
> > +};
> > +
> > +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > + const struct dphy_drv_data *drv_data = priv->drv_data;
> > + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> > + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> > + int i, hsfreq = 0;
> > +
> > + for (i = 0; i < num_hsfreq_ranges; i++) {
> > + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> > + hsfreq = hsfreq_ranges[i].cfg_bit;
> > + break;
> > + }
> > + }
> > + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> > + /* Disable lan turn around, which is ignored in receive mode */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> > +
> > + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> > +
> > + /* dphy start */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> > + usleep_range(100, 150);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> > + usleep_range(100, 150);
> > +
> > + /* set clock lane */
> > + /* HS hsfreq_range & lane 0 settle bypass */
> > + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane0 */
> > + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> > + /* HS RX Control of lane1 */
> > + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane2 */
> > + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane3 */
> > + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> > + /* HS RX Data Lanes Settle State Time Control */
> > + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> > + THS_SETTLE_COUNTER_THRESHOLD);
> > +
> > + /* Normal operation */
> > + mipidphy0_wr_reg(priv, 0x0, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > + const struct dphy_drv_data *drv_data = priv->drv_data;
> > + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> > + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> > + int i, hsfreq = 0;
> > +
> > + for (i = 0; i < num_hsfreq_ranges; i++) {
> > + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> > + hsfreq = hsfreq_ranges[i].cfg_bit;
> > + break;
> > + }
> > + }
> > + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> > + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> > + /* Disable lan turn around, which is ignored in receive mode */
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> > + GENMASK(sensor->lanes - 1, 0));
> > + /* dphy start */
> > + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> > + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + usleep_range(100, 150);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + usleep_range(100, 150);
> > +
> > + /* set clock lane */
> > + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> > + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> > + /* HS RX Data Lanes Settle State Time Control */
> > + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> > + THS_SETTLE_COUNTER_THRESHOLD);
> > +
> > + /* Normal operation */
> > + mipidphy1_wr_reg(priv, 0x0, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> > + .clks = rk3288_mipidphy_clks,
> > + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> > + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> > + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> > + .regs = rk3288_grf_dphy_regs,
> > +};
> > +
> > +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> > + .clks = rk3399_mipidphy_clks,
> > + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> > + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> > + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> > + .regs = rk3399_grf_dphy_regs,
> > +};
> > +
> > +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> > + {
> > + .compatible = "rockchip,rk3399-mipi-dphy",
> > + .data = &rk3399_mipidphy_drv_data,
> > + },
> > + {
> > + .compatible = "rockchip,rk3288-mipi-dphy",
> > + .data = &rk3288_mipidphy_drv_data,
> > + },
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>
> You can mode this just above the probe function to group it with the
> code that uses it.
>
> > +
> > +/* The .bound() notifier callback when a match is found */
> > +static int
> > +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> > + struct v4l2_subdev *sd,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct mipidphy_priv *priv = container_of(notifier,
> > + struct mipidphy_priv,
> > + notifier);
> > + struct sensor_async_subdev *s_asd = container_of(asd,
> > + struct sensor_async_subdev, asd);
> > + struct mipidphy_sensor *sensor;
> > + unsigned int pad, ret;
> > +
> > + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> > + return -EBUSY;
> > +
> > + sensor = &priv->sensors[priv->num_sensors++];
> > + sensor->lanes = s_asd->lanes;
> > + sensor->mbus = s_asd->mbus;
> > + sensor->sd = sd;
> > +
> > + for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
> > + if (sensor->sd->entity.pads[pad].flags
> > + & MEDIA_PAD_FL_SOURCE)
> > + break;
> > +
> > + if (pad == sensor->sd->entity.num_pads) {
> > + dev_err(priv->dev,
> > + "failed to find src pad for %s\n",
> > + sensor->sd->name);
> > +
> > + return -ENXIO;
> > + }
> > +
> > + ret = media_create_pad_link(
> > + &sensor->sd->entity, pad,
> > + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> > + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> > + if (ret) {
> > + dev_err(priv->dev,
> > + "failed to create link for %s\n",
> > + sensor->sd->name);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* The .unbind callback */
> > +static void
> > +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> > + struct v4l2_subdev *sd,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct mipidphy_priv *priv = container_of(notifier,
> > + struct mipidphy_priv,
> > + notifier);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> > +
> > + sensor->sd = NULL;
> > +}
> > +
> > +static const struct
> > +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> > + .bound = rockchip_mipidphy_notifier_bound,
> > + .unbind = rockchip_mipidphy_notifier_unbind,
> > +};
> > +
> > +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> > + struct v4l2_fwnode_endpoint *vep,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct sensor_async_subdev *s_asd =
> > + container_of(asd, struct sensor_async_subdev, asd);
> > + struct v4l2_mbus_config *config = &s_asd->mbus;
> > +
> > + if (vep->bus_type != V4L2_MBUS_CSI2) {
> > + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (vep->base.port != 0) {
> > + dev_err(dev, "The PHY has only port 0\n");
> > + return -EINVAL;
> > + }
> > +
> > + config->type = V4L2_MBUS_CSI2;
> > + config->flags = vep->bus.mipi_csi2.flags;
> > + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> > +
> > + switch (vep->bus.mipi_csi2.num_data_lanes) {
> > + case 1:
> > + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> > + break;
> > + case 2:
> > + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> > + break;
> > + case 3:
> > + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> > + break;
> > + case 4:
> > + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> > +{
> > + int ret;
> > +
> > + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> > + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> > + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> > + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> > +
> > + ret = media_entity_pads_init(&priv->sd.entity,
> > + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> > + priv->dev, &priv->notifier,
> > + sizeof(struct sensor_async_subdev), 0,
> > + rockchip_mipidphy_fwnode_parse);
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (!priv->notifier.num_subdevs)
> > + return -ENODEV; /* no endpoint */
> > +
> > + priv->sd.subdev_notifier = &priv->notifier;
> > + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> > + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> > + if (ret) {
> > + dev_err(priv->dev,
> > + "failed to register async notifier : %d\n", ret);
> > + v4l2_async_notifier_cleanup(&priv->notifier);
> > + return ret;
> > + }
> > +
> > + return v4l2_async_register_subdev(&priv->sd);
> > +}
> > +
> > +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct v4l2_subdev *sd;
> > + struct mipidphy_priv *priv;
> > + struct regmap *grf;
> > + struct resource *res;
> > + const struct of_device_id *of_id;
> > + const struct dphy_drv_data *drv_data;
> > + int i, ret;
> > +
> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > + priv->dev = dev;
> > +
> > + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> > + if (!of_id)
> > + return -EINVAL;
> > +
> > + grf = syscon_node_to_regmap(dev->parent->of_node);
> > + if (IS_ERR(grf)) {
> > + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> > + "rockchip,grf");
> > + if (IS_ERR(grf)) {
> > + dev_err(dev, "Can't find GRF syscon\n");
> > + return -ENODEV;
> > + }
> > + }
> > + priv->regmap_grf = grf;
> > +
> > + drv_data = of_id->data;
> > + for (i = 0; i < drv_data->num_clks; i++) {
> > + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> > +
> > + if (IS_ERR(priv->clks[i])) {
> > + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> > + return PTR_ERR(priv->clks[i]);
> > + }
> > + }
> > +
> > + priv->grf_regs = drv_data->regs;
> > + priv->drv_data = drv_data;
> > + priv->stream_on = mipidphy_txrx_stream_on;
> > + priv->txrx_base_addr = NULL;
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>
> This will result in an error being printed to the kernel log if res is
> NULL. The following (untested) code should fix it.
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (res) {
> priv->txrx_base_addr = devm_ioremap_resource(dev, res);
> if (IS_ERR(priv->txrx_base_addr))
> return PTR_ERR(priv->txrx_base_addr);
>
> priv->stream_on = mipidphy_txrx_stream_on;
> } else {
> priv->txrx_base_addr = NULL;
> priv->stream_on = mipidphy_rx_stream_on;
> }
>
> Furthermore, txrx_base_addr seems to be used to access the CSIHOST
> registers, part of the CSI-2 receiver (CSI host in the system connection
> description of the RK3288 datasheet, in the MIPI CSI PHY section), not
> the PHY itself. I'm afraid you'll have to redesign this to split the
> code between those two components. One option would be to have a CSI
> host DT node with a reg resource for the CSIHOST registers, modeled as a
> V4L2 subdev, and pointing to the DPHY using a phy-handle property. In
> the rkisp1 driver, I would then extract the CSI-2 receiver code to a
> separate subdev, and handle the PHY from that subdev, also with a
> phy-handle property in the rkisp1 DT node.
>
> > + if (IS_ERR(priv->txrx_base_addr))
> > + priv->stream_on = mipidphy_rx_stream_on;
> > +
> > + sd = &priv->sd;
> > + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> > + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> > + sd->dev = dev;
> > +
> > + platform_set_drvdata(pdev, &sd->entity);
> > +
> > + ret = rockchip_mipidphy_media_init(priv);
> > + if (ret < 0)
> > + return ret;
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> > +{
> > + struct media_entity *me = platform_get_drvdata(pdev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > +
> > + media_entity_cleanup(&sd->entity);
> > +
> > + pm_runtime_disable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> > + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> > + mipidphy_runtime_resume, NULL)
> > +};
> > +
> > +static struct platform_driver rockchip_isp_mipidphy_driver = {
> > + .probe = rockchip_mipidphy_probe,
> > + .remove = rockchip_mipidphy_remove,
> > + .driver = {
> > + .name = "rockchip-sy-mipi-dphy",
> > + .pm = &rockchip_mipidphy_pm_ops,
> > + .of_match_table = rockchip_mipidphy_match_id,
>
> Too much indentation.
>
> > + },
> > +};
> > +
> > +module_platform_driver(rockchip_isp_mipidphy_driver);
>
> dev.c also has a module_platform_driver(). As both are compiled in the
> same module, this results in a link error when compiling the drivers as
> a module. I would recommend separating it into two modules to fix this.
>
> > +MODULE_AUTHOR("Rockchip Camera/ISP team");
> > +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> > new file mode 100644
> > index 000000000000..c558791064a2
> > --- /dev/null
> > +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> > @@ -0,0 +1,15 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Rockchip MIPI Synopsys DPHY driver
> > + *
> > + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> > + */
> > +
> > +#ifndef __MIPI_DPHY_SY_H__
> > +#define __MIPI_DPHY_SY_H__
> > +
> > +#include <media/v4l2-subdev.h>
> > +
> > +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>
> This function doesn't seem to be used or defined anywhere, you can drop
> this file.
>
> > +
> > +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>
> --
> Regards,
>
> Laurent Pinchart
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format
2018-03-08 9:47 ` [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format Jacob Chen
@ 2019-04-26 20:34 ` Nicolas Dufresne
0 siblings, 0 replies; 50+ messages in thread
From: Nicolas Dufresne @ 2019-04-26 20:34 UTC (permalink / raw)
To: Jacob Chen, linux-rockchip
Cc: devicetree, eddie.cai.linux, heiko, Jacob Chen, jeffy.chen, zyc,
linux-kernel, tfiga, hans.verkuil, laurent.pinchart, sakari.ailus,
mchehab, zhengsq, linux-arm-kernel, linux-media
[-- Attachment #1.1: Type: text/plain, Size: 2252 bytes --]
Le jeudi 08 mars 2018 à 17:47 +0800, Jacob Chen a écrit :
> From: Shunqian Zheng <zhengsq@rock-chips.com>
>
> Add the Rockchip ISP1 specific processing parameter format
> V4L2_META_FMT_RK_ISP1_PARAMS and metadata format
> V4L2_META_FMT_RK_ISP1_STAT_3A for 3A.
>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
> include/uapi/linux/videodev2.h | 4 ++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 1be54429d601..bdd469290d66 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1257,6 +1257,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
> case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break;
> case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
> case V4L2_META_FMT_UVC: descr = "UVC payload header metadata"; break;
> + case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A params"; break;
> + case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A statistics"; break;
>
> default:
> /* Compressed formats */
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index e20d10df75c1..359ad8bbf9bf 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -698,6 +698,10 @@ struct v4l2_pix_format {
> #define V4L2_META_FMT_VSP1_HGT v4l2_fourcc('V', 'S', 'P', 'T') /* R-Car VSP1 2-D Histogram */
> #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */
>
> +/* Vendor specific - used for IPU3 camera sub-system */
Did you mean "RK_ISP1 camera sub-system" ?
> +#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */
> +#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A statistics */
> +
> /* priv field value to indicates that subsequent fields are valid. */
> #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
>
[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 50+ messages in thread
end of thread, other threads:[~2019-04-26 20:34 UTC | newest]
Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-08 9:47 [PATCH v6 00/17] Rockchip ISP1 Driver Jacob Chen
2018-03-08 9:47 ` [PATCH v6 01/17] media: videodev2.h, v4l2-ioctl: add rkisp1 meta buffer format Jacob Chen
2019-04-26 20:34 ` Nicolas Dufresne
2018-03-08 9:47 ` [PATCH v6 02/17] media: doc: add document for " Jacob Chen
2018-03-09 13:37 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 03/17] media: rkisp1: Add user space ABI definitions Jacob Chen
2018-03-09 13:34 ` Hans Verkuil
2018-04-24 4:28 ` Tomasz Figa
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2018-05-16 5:20 ` Laurent Pinchart
2018-05-16 14:39 ` Jacob Chen
2018-05-16 14:53 ` Jacob Chen
2018-05-16 15:15 ` Tomasz Figa
2019-03-10 17:49 ` Laurent Pinchart
2019-03-11 9:37 ` Tomasz Figa
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
2018-03-09 13:57 ` Hans Verkuil
2018-05-03 9:09 ` Baruch Siach
2018-05-07 6:13 ` Tomasz Figa
2018-05-07 6:38 ` Baruch Siach
2018-05-07 6:41 ` Tomasz Figa
2018-05-24 11:30 ` Baruch Siach
2018-05-25 4:35 ` Tomasz Figa
2018-03-08 9:47 ` [PATCH v6 06/17] media: rkisp1: add ISP1 statistics driver Jacob Chen
2018-03-09 13:59 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 07/17] media: rkisp1: add ISP1 params driver Jacob Chen
2018-03-09 14:03 ` Hans Verkuil
2018-03-08 9:47 ` [PATCH v6 09/17] media: rkisp1: add rockchip isp1 core driver Jacob Chen
2018-03-11 6:58 ` Baruch Siach
2018-05-16 8:08 ` Tomasz Figa
2018-03-08 9:48 ` [PATCH v6 10/17] dt-bindings: Document the Rockchip ISP1 bindings Jacob Chen
2018-03-08 9:48 ` [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
2018-03-08 9:48 ` [PATCH v6 12/17] ARM: dts: rockchip: add isp node for rk3288 Jacob Chen
2018-03-08 9:48 ` [PATCH v6 13/17] ARM: dts: rockchip: add rx0 mipi-phy " Jacob Chen
2018-03-08 9:48 ` [PATCH v6 14/17] ARM: dts: rockchip: Add dts mipi-dphy TXRX1 node " Jacob Chen
2018-03-08 9:48 ` [PATCH v6 15/17] arm64: dts: rockchip: add isp0 node for rk3399 Jacob Chen
2018-03-08 9:48 ` [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy " Jacob Chen
2018-03-08 9:48 ` [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
2018-03-08 10:29 ` [PATCH v6 00/17] Rockchip ISP1 Driver Tomasz Figa
2018-03-08 12:02 ` Baruch Siach
2018-03-09 0:53 ` Jacob Chen
2018-03-09 4:09 ` Baruch Siach
2018-03-09 5:05 ` Jacob Chen
2018-03-09 5:54 ` Baruch Siach
[not found] ` <20180308094807.9443-9-jacob-chen@iotwrt.com>
2018-03-09 14:07 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Hans Verkuil
2018-04-17 8:44 ` Tomasz Figa
2018-03-09 14:12 ` [PATCH v6 00/17] Rockchip ISP1 Driver Hans Verkuil
2018-09-03 13:07 ` Hans Verkuil
2018-09-03 13:50 ` Tomasz Figa
2018-09-03 14:15 ` Heiko Stuebner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).