Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / Atom feed
* [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver
       [not found] <jungo.lin@mediatek.com>
@ 2019-03-28  9:56 ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 01/12] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
                     ` (11 more replies)
  2019-06-11  3:53 ` [RFC, V3 0/9] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  1 sibling, 12 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

Hello,

This RFC patch series adding the driver for Pass 1 (P1) unit in
Mediatek's camera ISP system on mt8183 SoC, which
will be used in camera features of CrOS. It's the first time Mediatek
develops ISP kernel drivers based on V4L2 and media controller
framework. I posted the main part of the ISP Pass 1 driver as RFC to
discuss first and would like some review comments on the overall
architecture of the driver.

Pass 1 unit processes image signal from sensor devices and accepts the
tuning parameters to adjust the image quality. It performs optical
black correction, defect pixel correction, W/IR imbalance correction
and lens shading correction for RAW processing.

The driver is implemented with V4L2 and media controller framework so
we have the following entities to describe the ISP pass 1 path. (The
current metadata interface used in meta input and partial meta nodes
is only a temporary solution to kick off the driver development and is
not ready to be reviewed yet):

1. meta input (output video device): connects to ISP P1 sub device. It
accepts the tuning buffer from user.

2. ISP P1 (sub device): connects to partial meta 0/1/2/3,
main stream and packed out video devices. When processing an image,
Pass 1 hardware supports multiple output images with different sizes
and formats so it needs two capture video devices ("main stream" and
"packed out") to return the image data to the user.

3. main stream (capture video device): return the processed image data
which is used in capture scenario.

4. packed out (capture video device): return the processed image data
which is used in preview scenario.

5. partial meta 0 (capture video device): return the AE/AWB statistics.

6. partial meta 1 (capture video device): return the AF statistics.

7. partial meta 2 (capture video device): return the local contrast
   enhanced statistics.
   
8. partial meta 3 (capture video device): return the local motion
   vector statistics.

The overall patches of the series is:

* Patch 1~5 are dt-bindings & dts information related to
  ISP P1 driver & shared memory.
  
* Patch 6 is Kconfig configuration for ISP P1 driver.

* Patch 7 extends the original V4L2 image & meta formats.

* Patch 8 add private v4l2 control ID for ISP P1 driver.

* Patch 9 provides V4L2 utility functions & default video devices
  configuration for ISP P1 driver.
  
* Patch 10 is the heart of ISP P1 driver. It handles the ISP
  HW configuration, provides interrupt handling and initializes
  the V4L2 device nodes and other functions.
  
* Patch 11 adds communication with the co-processor on the SoC through
  the SCP driver. 
The SCP driver path is listed below:
<URL: https://patchwork.kernel.org/cover/10872547/>

* Patch 12 is current shared memory usage implementation and will be
revised based on Tomasz's suggestion in later version.

Here is ISP P1 media topology:
It is included the main/sub sensor & sen-inf sub-devices which are
implemented in below patch:
<URL: https://patchwork.kernel.org/cover/10852957/>

media-ctl -d /dev/media0 -p

Media controller API version 4.19.23

Media device information
------------------------
driver          mtk-cam
model           MTK-ISP-P1-V4L2
serial          
bus info        platform:1a000000.camisp
hw revision     0x0
driver version  4.19.23

Device topology
- entity 1: MTK-ISP-P1-V4L2 (12 pads, 8 links)
            type V4L2 subdev subtype Unknown flags 0
            device node name /dev/v4l-subdev4
	pad0: Sink
		<- "MTK-ISP-P1-V4L2 meta input":0 []
	pad1: Source
		-> "MTK-ISP-P1-V4L2 main stream":0 [ENABLED]
	pad2: Source
		-> "MTK-ISP-P1-V4L2 packed out":0 [ENABLED]
	pad3: Source
		-> "MTK-ISP-P1-V4L2 partial meta 0":0 []
	pad4: Source
		-> "MTK-ISP-P1-V4L2 partial meta 1":0 []
	pad5: Source
		-> "MTK-ISP-P1-V4L2 partial meta 2":0 [DYNAMIC]
	pad6: Source
		-> "MTK-ISP-P1-V4L2 partial meta 3":0 [DYNAMIC]
	pad7: Source
	pad8: Source
	pad9: Source
	pad10: Source
	pad11: Sink
		<- "seninf":4 []

- entity 14: MTK-ISP-P1-V4L2 meta input (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video0
	pad0: Sink
		-> "MTK-ISP-P1-V4L2":0 []

- entity 20: MTK-ISP-P1-V4L2 main stream (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video1
	pad0: Source
		<- "MTK-ISP-P1-V4L2":1 [ENABLED]

- entity 26: MTK-ISP-P1-V4L2 packed out (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video2
	pad0: Source
		<- "MTK-ISP-P1-V4L2":2 [ENABLED]

- entity 32: MTK-ISP-P1-V4L2 partial meta 0 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video3
	pad0: Source
		<- "MTK-ISP-P1-V4L2":3 []

- entity 38: MTK-ISP-P1-V4L2 partial meta 1 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video4
	pad0: Source
		<- "MTK-ISP-P1-V4L2":4 []

- entity 44: MTK-ISP-P1-V4L2 partial meta 2 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video5
	pad0: Source
		<- "MTK-ISP-P1-V4L2":5 [DYNAMIC]

- entity 50: MTK-ISP-P1-V4L2 partial meta 3 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video6
	pad0: Source
		<- "MTK-ISP-P1-V4L2":6 [DYNAMIC]

- entity 56: sensor_main (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev5
	pad0: Source
		[fmt:SBGGR10_1X10/2592x1944 field:none colorspace:srgb]
		-> "seninf":0 []

- entity 58: sensor_sub (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev6
	pad0: Source
		[fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
		-> "seninf":1 []

- entity 60: seninf (12 pads, 3 links)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev7
	pad0: Sink
		<- "sensor_main":0 []
	pad1: Sink
		<- "sensor_sub":0 []
	pad2: Sink
	pad3: Sink
	pad4: Source
		-> "MTK-ISP-P1-V4L2":11 []
	pad5: Source
	pad6: Source
	pad7: Source
	pad8: Source
	pad9: Source
	pad10: Source
	pad11: Source

===========
= history =
===========

version 1:
 - Revised driver soruces based on Tomasz's comments including
   part1/2/3/4 in RFC V0 patch.
 - Remove DMA cache mechanism.
   Support two new video devices (LCSO/LMVO) for advance camera
   features.
 - Fixed v4l2-compliance test failure items.
 - Add private controls for Mediatek camera middleware.
 - Replace VPU driver's APIs with new SCP driver interface for
   co-processor communication.
 - Refactoring mtk_cam_scp.c to use ring-buffers mechanism for IPI
   commands RX handling.
 - Fix internal bugs.
    
TODOs:
 - Remove mtk_cam_smem_drv.c & mtk_cam_smem.h and implement DMA pool
   for shared memory management.
 - Revised file names.
 - Support non frame-sync AFO/AAO DMA buffers

version 0:
- Initial submission

The v4l2-compliance is built with the below lastest patch.
https://git.linuxtv.org/v4l-utils.git/commit/utils/v4l2-compliance?id=2aff3ef768c42cfdbb31d143ee2286a6b46e9db0

Note 1.
Revised testRequests function to bypass V4L2_CTRL_FLAG_READ_ONLY to
avoid VIDIOC_S_EXT_CTRLS failure due to EACASS with read only flag.

[Code]
	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ||
		qctrl.flags & V4L2_CTRL_FLAG_WRITE_ONLY)
		continue;

Note 2.
Before passing streaming on testing, need to setup media links for
/dev/video1 & /dev/video2.

/usr/bin/media-ctl -i
Enter a link to modify or enter to stop
1:1->20:0[1]
1:1->20:0[1]
Enter a link to modify or enter to stop
1:2->26:0[1]
1:2->26:0[1]
Enter a link to modify or enter to stop

Note 3.
Those 24 failures are belonged to same root cause which is caused
by seninf driver. These failures will be fixed in the next
seninf patch set which is mentioned above. Moreover, some OV sensor
issues are already fixed and will be also submitted with seninf
patch set together in the next version.

v4l2-compliance test output:
/usr/bin/v4l2-compliance -m /dev/media0

v4l2-compliance SHA: not available, 32 bits

Compliance test for mtk-cam device /dev/media0:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23

Required ioctls:
	test MEDIA_IOC_DEVICE_INFO: OK

Allow for multiple opens:
	test second /dev/media0 open: OK
	test MEDIA_IOC_DEVICE_INFO: OK
	test for unlimited opens: OK

Media Controller ioctls:
	test MEDIA_IOC_G_TOPOLOGY: OK
	Entities: 11 Interfaces: 11 Pads: 33 Links: 21
	test MEDIA_IOC_ENUM_ENTITIES/LINKS: OK
	test MEDIA_IOC_SETUP_LINK: OK

Total for mtk-cam device /dev/media0: 7, Succeeded: 7, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video0:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	Capabilities     : 0x8c200000
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x0c200000
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000010
	Type             : V4L Video
Entity Info:
	ID               : 0x0000000e (14)
	Name             : MTK-ISP-P1-V4L2 meta input
	Function         : V4L2 I/O
	Pad 0x0100000f   : 0: Sink
	  Link 0x02000012: to remote pad 0x1000002 of entity 'MTK-ISP-P1-V4L2': Data

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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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 Su/pported)
	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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video0: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video1:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	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      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000016
	Type             : V4L Video
Entity Info:
	ID               : 0x00000014 (20)
	Name             : MTK-ISP-P1-V4L2 main stream
	Function         : V4L2 I/O
	Pad 0x01000015   : 0: Source
	  Link 0x02000018: from remote pad 0x1000003 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video1 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: 1 Private Controls: 2

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)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	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
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video1: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video2:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	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      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x0300001c
	Type             : V4L Video
Entity Info:
	ID               : 0x0000001a (26)
	Name             : MTK-ISP-P1-V4L2 packed out
	Function         : V4L2 I/O
	Pad 0x0100001b   : 0: Source
	  Link 0x0200001e: from remote pad 0x1000004 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video2 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: 1 Private Controls: 2

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)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	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
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video2: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video3:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000022
	Type             : V4L Video
Entity Info:
	ID               : 0x00000020 (32)
	Name             : MTK-ISP-P1-V4L2 partial meta 0
	Function         : V4L2 I/O
	Pad 0x01000021   : 0: Source
	  Link 0x02000024: from remote pad 0x1000005 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video3 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video3: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video4:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000028
	Type             : V4L Video
Entity Info:
	ID               : 0x00000026 (38)
	Name             : MTK-ISP-P1-V4L2 partial meta 1
	Function         : V4L2 I/O
	Pad 0x01000027   : 0: Source
	  Link 0x0200002a: from remote pad 0x1000006 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video4 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video4: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video5:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x0300002e
	Type             : V4L Video
Entity Info:
	ID               : 0x0000002c (44)
	Name             : MTK-ISP-P1-V4L2 partial meta 2
	Function         : V4L2 I/O
	Pad 0x0100002d   : 0: Source
	  Link 0x02000030: from remote pad 0x1000007 of entity 'MTK-ISP-P1-V4L2': Data, Dynamic

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video5 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video5: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video6:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.23
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000034
	Type             : V4L Video
Entity Info:
	ID               : 0x00000032 (50)
	Name             : MTK-ISP-P1-V4L2 partial meta 3
	Function         : V4L2 I/O
	Pad 0x01000033   : 0: Source
	  Link 0x02000036: from remote pad 0x1000008 of entity 'MTK-ISP-P1-V4L2': Data, Dynamic

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video6 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video6: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev4:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x03000049
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000001 (1)
	Name             : MTK-ISP-P1-V4L2
	Function         : Video Statistics
	Pad 0x01000002   : 0: Sink
	  Link 0x02000012: from remote pad 0x100000f of entity 'MTK-ISP-P1-V4L2 meta input': Data
	Pad 0x01000003   : 1: Source
	  Link 0x02000018: to remote pad 0x1000015 of entity 'MTK-ISP-P1-V4L2 main stream': Data, Enabled
	Pad 0x01000004   : 2: Source
	  Link 0x0200001e: to remote pad 0x100001b of entity 'MTK-ISP-P1-V4L2 packed out': Data, Enabled
	Pad 0x01000005   : 3: Source
	  Link 0x02000024: to remote pad 0x1000021 of entity 'MTK-ISP-P1-V4L2 partial meta 0': Data
	Pad 0x01000006   : 4: Source
	  Link 0x0200002a: to remote pad 0x1000027 of entity 'MTK-ISP-P1-V4L2 partial meta 1': Data
	Pad 0x01000007   : 5: Source
	  Link 0x02000030: to remote pad 0x100002d of entity 'MTK-ISP-P1-V4L2 partial meta 2': Data, Dynamic
	Pad 0x01000008   : 6: Source
	  Link 0x02000036: to remote pad 0x1000033 of entity 'MTK-ISP-P1-V4L2 partial meta 3': Data, Dynamic
	Pad 0x01000009   : 7: Source
	Pad 0x0100000a   : 8: Source
	Pad 0x0100000b   : 9: Source
	Pad 0x0100000c   : 10: Source
	Pad 0x0100000d   : 11: Sink
	  Link 0x02000055: from remote pad 0x1000041 of entity 'seninf': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev4 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 1):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 2):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 3):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 11):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev4: 125, Succeeded: 125, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev5:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x0300004b
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000038 (56)
	Name             : sensor_main
	Function         : Camera Sensor
	Pad 0x01000039   : 0: Source
	  Link 0x02000051: to remote pad 0x100003d of entity 'seninf': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev5 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 11 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev5: 48, Succeeded: 48, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev6:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x0300004d
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x0000003a (58)
	Name             : sensor_sub
	Function         : Camera Sensor
	Pad 0x0100003b   : 0: Source
	  Link 0x02000053: to remote pad 0x100003e of entity 'seninf': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev6 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 10 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev6: 48, Succeeded: 48, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev7:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.23
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.23
Interface Info:
	ID               : 0x0300004f
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x0000003c (60)
	Name             : seninf
	Function         : Camera Sensor
	Pad 0x0100003d   : 0: Sink
	  Link 0x02000051: from remote pad 0x1000039 of entity 'sensor_main': Data
	Pad 0x0100003e   : 1: Sink
	  Link 0x02000053: from remote pad 0x100003b of entity 'sensor_sub': Data
	Pad 0x0100003f   : 2: Sink
	Pad 0x01000040   : 3: Sink
	Pad 0x01000041   : 4: Source
	  Link 0x02000055: to remote pad 0x100000d of entity 'MTK-ISP-P1-V4L2': Data
	Pad 0x01000042   : 5: Source
	Pad 0x01000043   : 6: Source
	Pad 0x01000044   : 7: Source
	Pad 0x01000045   : 8: Source
	Pad 0x01000046   : 9: Source
	Pad 0x01000047   : 10: Source
	Pad 0x01000048   : 11: Source

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev7 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 1):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 2):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 3):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 11):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Try VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
		fail: v4l2-test-subdevs.cpp(341): doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY
	test Active VIDIOC_SUBDEV_G/S_FMT: FAIL
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 2 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev7: 125, Succeeded: 101, Failed: 24, Warnings: 0

Grand Total for mtk-cam device /dev/media0: 668, Succeeded: 644, Failed: 24, Warnings: 0

Jungo Lin (12):
  dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
  dts: arm64: mt8183: Add ISP Pass 1 shared memory node
  dt-bindings: mt8183: Added cam-smem dt-bindings
  dt-bindings: mt8183: Added camera ISP Pass 1
  dts: arm64: mt8183: Add ISP Pass 1 nodes
  media: platform: Add Mediatek ISP Pass 1 driver Kconfig
  media: platform: Add Mediatek ISP P1 image & meta formats
  media: platform: Add Mediatek ISP P1 private control
  media: platform: Add Mediatek ISP P1 V4L2 functions
  media: platform: Add Mediatek ISP P1 device driver
  media: platform: Add Mediatek ISP P1 SCP communication
  media: platform: Add Mediatek ISP P1 shared memory driver

 .../bindings/media/mediatek,cam_smem.txt           |   32 +
 .../devicetree/bindings/media/mediatek,camisp.txt  |   59 +
 .../mediatek,reserve-memory-cam_smem.txt           |   44 +
 arch/arm64/boot/dts/mediatek/mt8183.dtsi           |   55 +
 drivers/media/platform/Kconfig                     |    2 +
 drivers/media/platform/mtk-isp/Kconfig             |   21 +
 drivers/media/platform/mtk-isp/isp_50/cam/Makefile |   19 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c     |  133 +++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h     |   32 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h      |  116 ++
 .../mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c      |  302 +++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.c      |  525 +++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.h      |  166 +++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-regs.h     |  147 +++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c      |  488 ++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h      |  215 ++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c |  398 +++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-smem.h     |   25 +
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c         | 1182 ++++++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h         |   43 +
 .../media/platform/mtk-isp/isp_50/cam/mtk_cam.c    | 1098 ++++++++++++++++++
 .../media/platform/mtk-isp/isp_50/cam/mtk_cam.h    |  288 +++++
 include/uapi/linux/v4l2-controls.h                 |    4 +
 include/uapi/linux/videodev2.h                     |   20 +
 24 files changed, 5414 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,cam_smem.txt
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt
 create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam_smem.txt
 create mode 100644 drivers/media/platform/mtk-isp/Kconfig
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h

-- 
1.9.1



_______________________________________________
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] 104+ messages in thread

* [RFC V1 01/12] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 02/12] dts: arm64: mt8183: Add ISP Pass 1 shared memory node Jungo Lin
                     ` (10 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds the binding for describing the reserved shared memory
used to exchange configuration and tuning data between the
co-processor and Pass 1 (P1) unit of the camera ISP system on
Mediatek SoCs.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../mediatek,reserve-memory-cam_smem.txt           | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam_smem.txt

diff --git a/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam_smem.txt b/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam_smem.txt
new file mode 100644
index 0000000..05c1bf1
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam_smem.txt
@@ -0,0 +1,44 @@
+Mediatek ISP Pass 1 Shared Memory binding
+
+This binding describes the shared memory, which serves the purpose of
+describing the shared memory region used to exchange data between Pass 1
+unit of Image Signal Processor (ISP) and the co-processor in Mediatek
+SoCs.
+
+The co-processor doesn't have the iommu so we need to use the physical
+address to access the shared buffer in the firmware.
+
+The Pass 1 unit of ISP can access memory through the iommu so it
+uses the dma address to access the memory region.
+(See iommu/mediatek,iommu.txt for the detailed description of Mediatek IOMMU)
+
+
+Required properties:
+
+- compatible: must be "mediatek,reserve-memory-cam_smem"
+
+- reg: required for static allocation (see reserved-memory.txt for
+  the detailed usage)
+
+- alloc-range: required for dynamic allocation. The range must
+  between 0x00000400 and 0x100000000 due to the co-processer's
+  addressing limitation
+
+- size: required for dynamic allocation. The unit is bytes.
+
+
+Example:
+
+The following example shows the ISP Pass1 shared memory setup for MT8183.
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		reserve-memory-cam_smem {
+			compatible = "mediatek,reserve-memory-cam_smem";
+			size = <0 0x1400000>;
+			alignment = <0 0x1000>;
+			alloc-ranges = <0 0x40000000 0 0x50000000>;
+		};
+	};
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 02/12] dts: arm64: mt8183: Add ISP Pass 1 shared memory node
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  2019-03-28  9:56   ` [RFC V1 01/12] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 03/12] dt-bindings: mt8183: Added cam-smem dt-bindings Jungo Lin
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds a shared memory region used on mt8183 for
exchanging tuning data between co-processor and the
Pass 1 unit of the camera ISP system.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index 75c4881..c73f7ff 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -133,6 +133,19 @@
 		clock-output-names = "clk26m";
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		reserve-memory-cam_smem {
+			compatible = "mediatek,reserve-memory-cam_smem";
+			no-map;
+			size = <0 0x01400000>; /*20 MB share mem size */
+			alignment = <0 0x1000>;
+			alloc-ranges = <0 0x40000000 0 0x10000000>;
+		};
+	};
+
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupt-parent = <&gic>;
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 03/12] dt-bindings: mt8183: Added cam-smem dt-bindings
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  2019-03-28  9:56   ` [RFC V1 01/12] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
  2019-03-28  9:56   ` [RFC V1 02/12] dts: arm64: mt8183: Add ISP Pass 1 shared memory node Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 04/12] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
                     ` (8 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds the DT binding documentation for the shared memory
between Pass 1 unit of the camera ISP system and the co-processor
in Mediatek SoCs.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../bindings/media/mediatek,cam_smem.txt           | 32 ++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,cam_smem.txt

diff --git a/Documentation/devicetree/bindings/media/mediatek,cam_smem.txt b/Documentation/devicetree/bindings/media/mediatek,cam_smem.txt
new file mode 100644
index 0000000..d34006a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,cam_smem.txt
@@ -0,0 +1,32 @@
+Mediatek Camera ISP Pass 1 Shared Memory Device
+
+Mediatek Camera ISP Pass 1 Shared Memory Device is used to manage shared
+memory among CPU, Camera ISP Pass 1 hardware and coprocessor. The Camera
+ISP Pass 1 is a hardware unit for processing image signal from the image
+sensor. Camera ISP Pass 1 is responsible for RAW processing and 3A statistics.
+
+It is associated with a reserved memory region
+(Please see Documentation/devicetree/bindings/reserved-memory/mediatek,
+reserve-memory-cam_smem.txt) and and provides the context to
+allocate memory with dma addresses.
+
+Required properties:
+- compatible: must be "mediatek,mt8183-cam_smem" for MT8183.
+
+- iommus: shall point to the respective IOMMU block with master port
+  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+
+- mediatek,larb: must contain the local arbiters in the current SoCs, see
+  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+  for details.
+
+Example:
+	cam_smem: cam_smem {
+		compatible = "mediatek,mt8183-cam_smem";
+		mediatek,larb = <&larb3>,
+				<&larb6>;
+		iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+			 <&iommu M4U_PORT_CAM_LSCI1>,
+			 <&iommu M4U_PORT_CAM_BPCI>;
+	};
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 04/12] dt-bindings: mt8183: Added camera ISP Pass 1
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (2 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 03/12] dt-bindings: mt8183: Added cam-smem dt-bindings Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 05/12] dts: arm64: mt8183: Add ISP Pass 1 nodes Jungo Lin
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds DT binding document for the Pass 1 (P1) unit in
Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data
out from the sensor interface, applies ISP effects and writes the
image data to DRAM.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek,camisp.txt  | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt

diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
new file mode 100644
index 0000000..5af1e3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
@@ -0,0 +1,59 @@
+* Mediatek Image Signal Processor Pass 1 (ISP P1)
+
+The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out
+from the sensor interface, applies ISP effects and writes the image data
+to DRAM. Furthermore, Pass 1 unit has the ability to output two different
+resolutions frames at the same time to increase the performance of the
+camera application.
+
+Required properties:
+- compatible: must be "mediatek,mt8183-camisp" for MT8183.
+- reg: must contain an entry for each entry in reg-names.
+- interrupts: interrupt number to the cpu.
+- iommus: shall point to the respective IOMMU block with master port
+  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- power-domains : a phandle to the power domain of this local arbiter.
+- mediatek,smi : a phandle to the smi_common node.
+- clocks: device clocks, see
+  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- clock-names: must be "CAMSYS_CAM_CGPDN" and "CAMSYS_CAMTG_CGPDN".
+- mediatek,larb: must contain the local arbiters in the current SOCs, see
+  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+  for details.
+- mediatek,scp : the node of system control processor (SCP).
+- smem_device : the shared memory device managing the shared memory between
+  Pass 1 unit and the video processor unit.
+
+Example:
+	camisp: camisp@1a000000 {
+		compatible = "mediatek,mt8183-camisp", "syscon";
+		reg = <0 0x1a000000 0 0x1000>,
+		      <0 0x1a003000 0 0x1000>,
+		      <0 0x1a004000 0 0x2000>,
+		      <0 0x1a006000 0 0x2000>;
+		reg-names = "camisp",
+		            "cam1",
+		            "cam2",
+		            "cam3";
+		interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+		interrupt-names = "cam1",
+				  "cam2",
+				  "cam3";
+		iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+			 <&iommu M4U_PORT_CAM_LSCI1>,
+			 <&iommu M4U_PORT_CAM_BPCI>;
+		#clock-cells = <1>;
+		power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
+		/* Camera CCF */
+		clocks = <&camsys CLK_CAM_CAM>,
+			 <&camsys CLK_CAM_CAMTG>;
+		clock-names = "CAMSYS_CAM_CGPDN",
+			      "CAMSYS_CAMTG_CGPDN";
+		mediatek,larb = <&larb3>,
+				<&larb6>;
+		mediatek,scp = <&scp>;
+		smem_device = <&cam_smem>;
+	};
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 05/12] dts: arm64: mt8183: Add ISP Pass 1 nodes
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (3 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 04/12] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 06/12] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Jungo Lin
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

Add nodes for Pass 1 unit of Mediatek's camera ISP system.
Pass 1 unit embedded in Mediatek SoCs, works with the
co-processor to process image signal from the image sensor
and output RAW data.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 42 ++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index c73f7ff..7df39bd 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -382,5 +382,47 @@
 			reg = <0 0x1a000000 0 0x1000>;
 			#clock-cells = <1>;
 		};
+
+		cam_smem: cam_smem {
+			compatible = "mediatek,mt8183-cam_smem";
+			mediatek,larb = <&larb3>,
+					<&larb6>;
+			iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+				 <&iommu M4U_PORT_CAM_LSCI1>,
+				 <&iommu M4U_PORT_CAM_BPCI>;
+		};
+
+		camisp: camisp@1a000000 {
+			compatible = "mediatek,mt8183-camisp", "syscon";
+			reg = <0 0x1a000000 0 0x1000>,
+			      <0 0x1a003000 0 0x1000>,
+			      <0 0x1a004000 0 0x2000>,
+			      <0 0x1a006000 0 0x2000>;
+			reg-names = "camisp",
+				    "cam1",
+				    "cam2",
+				    "cam3";
+			interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+			interrupt-names = "cam1",
+					  "cam2",
+					  "cam3";
+			iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+				 <&iommu M4U_PORT_CAM_LSCI1>,
+				 <&iommu M4U_PORT_CAM_BPCI>;
+			#clock-cells = <1>;
+			power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
+			/* Camera CCF */
+			clocks = <&camsys CLK_CAM_CAM>,
+				 <&camsys CLK_CAM_CAMTG>;
+			clock-names = "CAMSYS_CAM_CGPDN",
+				      "CAMSYS_CAMTG_CGPDN";
+			mediatek,larb = <&larb3>,
+					<&larb6>;
+			mediatek,scp = <&scp>;
+			smem_device = <&cam_smem>;
+		};
+
 	};
 };
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 06/12] media: platform: Add Mediatek ISP Pass 1 driver Kconfig
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (4 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 05/12] dts: arm64: mt8183: Add ISP Pass 1 nodes Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 07/12] media: platform: Add Mediatek ISP P1 image & meta formats Jungo Lin
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds Kconfig for Pass 1 (P1) unit driver of Mediatek's
camera ISP system. ISP P1 unit is embedded in Mediatek SoCs. It
provides RAW processing which includes optical black correction,
defect pixel correction, W/IR imbalance correction and lens
shading correction.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 drivers/media/platform/Kconfig         |  2 ++
 drivers/media/platform/mtk-isp/Kconfig | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/Kconfig

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 4acbed1..7be62e0 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -32,6 +32,8 @@ source "drivers/media/platform/davinci/Kconfig"
 
 source "drivers/media/platform/omap/Kconfig"
 
+source "drivers/media/platform/mtk-isp/Kconfig"
+
 config VIDEO_ASPEED
 	tristate "Aspeed AST2400 and AST2500 Video Engine driver"
 	depends on VIDEO_V4L2
diff --git a/drivers/media/platform/mtk-isp/Kconfig b/drivers/media/platform/mtk-isp/Kconfig
new file mode 100644
index 0000000..9932563
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/Kconfig
@@ -0,0 +1,21 @@
+config VIDEO_MEDIATEK_ISP_PASS1_SUPPORT
+	bool "Mediatek pass 1 image processing function"
+
+	select DMA_SHARED_BUFFER
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_V4L2
+	select VIDEOBUF2_MEMOPS
+	select VIDEOBUF2_VMALLOC
+	select MEDIA_CONTROLLER
+
+	default n
+	help
+		Pass 1 driver controls 3A (autofocus, exposure,
+		and white balance) with tuning parameters and outputs
+		the capture image buffers in Mediatek's camera system.
+
+		Choose y if you want to use Mediatek SoCs to create image
+		capture application such as video recording and still image
+		capture.
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 07/12] media: platform: Add Mediatek ISP P1 image & meta formats
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (5 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 06/12] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 08/12] media: platform: Add Mediatek ISP P1 private control Jungo Lin
                     ` (4 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

Add packed/unpacked/full-g bayer format with 8/10/12/14 bit
for image output. Add Pass 1 (P1) specific meta formats for
parameter processing and 3A/other statistics.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 include/uapi/linux/videodev2.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 1db220d..b79046d 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -711,6 +711,20 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
 #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
 
+/* Vendor specific - Mediatek ISP compressed formats */
+#define V4L2_PIX_FMT_MTISP_U8	v4l2_fourcc('M', 'T', 'U', '8') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U10  v4l2_fourcc('M', 'T', 'U', 'A') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U12  v4l2_fourcc('M', 'T', 'U', 'C') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U14  v4l2_fourcc('M', 'T', 'U', 'E') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_B8	v4l2_fourcc('M', 'T', 'B', '8') /* Packed   bayer format,  8-bit */
+#define V4L2_PIX_FMT_MTISP_B10  v4l2_fourcc('M', 'T', 'B', 'A') /* Packed   bayer format, 10-bit */
+#define V4L2_PIX_FMT_MTISP_B12  v4l2_fourcc('M', 'T', 'B', 'C') /* Packed   bayer format, 12-bit */
+#define V4L2_PIX_FMT_MTISP_B14  v4l2_fourcc('M', 'T', 'B', 'E') /* Packed   bayer format, 14-bit */
+#define V4L2_PIX_FMT_MTISP_F8	v4l2_fourcc('M', 'T', 'F', '8') /* Full-G   bayer format,  8-bit */
+#define V4L2_PIX_FMT_MTISP_F10  v4l2_fourcc('M', 'T', 'F', 'A') /* Full-G   bayer format, 10-bit */
+#define V4L2_PIX_FMT_MTISP_F12  v4l2_fourcc('M', 'T', 'F', 'C') /* Full-G   bayer format, 12-bit */
+#define V4L2_PIX_FMT_MTISP_F14  v4l2_fourcc('M', 'T', 'F', 'E') /* Full-G   bayer format, 14-bit */
+
 /* SDR formats - used only for Software Defined Radio devices */
 #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
 #define V4L2_SDR_FMT_CU16LE       v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
@@ -732,6 +746,12 @@ 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 */
 #define V4L2_META_FMT_D4XX        v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
+/* Vendor specific - Mediatek ISP parameters for firmware */
+#define V4L2_META_FMT_MTISP_PARAMS v4l2_fourcc('M', 'T', 'f', 'p') /* ISP tuning parameters */
+#define V4L2_META_FMT_MTISP_3A	   v4l2_fourcc('M', 'T', 'f', 'a') /* AE/AWB histogram */
+#define V4L2_META_FMT_MTISP_AF	   v4l2_fourcc('M', 'T', 'f', 'f') /* AF histogram */
+#define V4L2_META_FMT_MTISP_LCS	   v4l2_fourcc('M', 'T', 'f', 'c') /* Local contrast enhanced statistics */
+#define V4L2_META_FMT_MTISP_LMV	   v4l2_fourcc('M', 'T', 'f', 'm') /* Local motion vector histogram */
 
 /* priv field value to indicates that subsequent fields are valid. */
 #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 08/12] media: platform: Add Mediatek ISP P1 private control
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (6 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 07/12] media: platform: Add Mediatek ISP P1 image & meta formats Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 09/12] media: platform: Add Mediatek ISP P1 V4L2 functions Jungo Lin
                     ` (3 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

Reserved Mediatek ISP P1 private control number with 16.
Moreover, add two private controls for ISP P1 user space
usage.

1. V4L2_CID_PRIVATE_GET_BIN_INFO
- Provide the image output width & height in case
camera binning mode is enabled.

2. V4L2_CID_PRIVATE_RAW_PATH
- Export the path control of the main stream to user space.
One is pure raw and the other is processing raw.
The default image path is pure raw.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c     | 133 +++++++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h     |  32 +++++
 include/uapi/linux/v4l2-controls.h                 |   4 +
 3 files changed, 169 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
new file mode 100644
index 0000000..455216a
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "mtk_cam-dev.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam.h"
+
+static int handle_ctrl_get_bin_info(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	const unsigned int idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	struct v4l2_format *imgo_fmt = &cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+	unsigned int width, height;
+
+	width = imgo_fmt->fmt.pix_mp.width;
+	height = imgo_fmt->fmt.pix_mp.height;
+
+	dev_dbg(&cam_dev->pdev->dev, "Get bin info w*h:(%d*%d)",
+		width, height);
+
+	ctrl->val = (width << 16) | height;
+
+	return 0;
+}
+
+static int handle_ctrl_get_raw_path(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
+
+	ctrl->val = p1_dev->isp_ctx.isp_raw_path;
+
+	dev_dbg(&cam_dev->pdev->dev, "Get raw path:%d", ctrl->val);
+
+	return 0;
+}
+
+static int handle_ctrl_set_raw_path(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
+
+	p1_dev->isp_ctx.isp_raw_path = ctrl->val;
+	dev_dbg(&cam_dev->pdev->dev, "Set raw path:%d", ctrl->val);
+	return 0;
+}
+
+static int mtk_cam_dev_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_GET_BIN_INFO:
+		handle_ctrl_get_bin_info(ctrl);
+		break;
+	case V4L2_CID_PRIVATE_RAW_PATH:
+		handle_ctrl_get_raw_path(ctrl);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mtk_cam_dev_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_RAW_PATH:
+		return handle_ctrl_set_raw_path(ctrl);
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct v4l2_ctrl_ops mtk_cam_dev_ctrl_ops = {
+	.g_volatile_ctrl = mtk_cam_dev_g_ctrl,
+	.s_ctrl = mtk_cam_dev_s_ctrl,
+};
+
+struct v4l2_ctrl_config mtk_cam_controls[] = {
+	{
+	.ops = &mtk_cam_dev_ctrl_ops,
+	.id = V4L2_CID_PRIVATE_GET_BIN_INFO,
+	.name = "MTK CAM GET BIN INFO",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = (IMG_MIN_WIDTH << 16) | IMG_MIN_HEIGHT,
+	.max = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
+	.step = 1,
+	.def = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
+	.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+	.ops = &mtk_cam_dev_ctrl_ops,
+	.id = V4L2_CID_PRIVATE_RAW_PATH,
+	.name = "MTK CAM RAW PATH",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+	},
+};
+
+int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
+		      struct v4l2_ctrl_handler *hdl)
+{
+	unsigned int i;
+
+	/* Initialized HW controls, allow V4L2_CID_MTK_CAM_MAX ctrls */
+	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_CAM_MAX);
+	if (hdl->error) {
+		v4l2_ctrl_handler_free(hdl);
+		return hdl->error;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mtk_cam_controls); i++)
+		v4l2_ctrl_new_custom(hdl, &mtk_cam_controls[i], cam_dev);
+
+	dev_info(&cam_dev->pdev->dev, "%s done", __func__);
+	return 0;
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
new file mode 100644
index 0000000..74a6538
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_CTRL_H__
+#define __MTK_CAM_CTRL_H__
+
+#include <media/v4l2-ctrls.h>
+
+#define V4L2_CID_MTK_CAM_PRIVATE_CAM  V4L2_CID_USER_MTK_CAM_BASE
+#define V4L2_CID_PRIVATE_GET_BIN_INFO \
+	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 1)
+#define V4L2_CID_PRIVATE_RAW_PATH \
+	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 2)
+
+#define V4L2_CID_MTK_CAM_MAX	16
+
+int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
+		      struct v4l2_ctrl_handler *hdl);
+
+#endif /* __MTK_CAM_CTRL_H__ */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 06479f2..cbe8f5f 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -192,6 +192,10 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
 
+/* The base for the mediatek ISP Pass 1 driver controls */
+/* We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_MTK_CAM_BASE		(V4L2_CID_USER_BASE + 0x10c0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 09/12] media: platform: Add Mediatek ISP P1 V4L2 functions
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (7 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 08/12] media: platform: Add Mediatek ISP P1 private control Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 10/12] media: platform: Add Mediatek ISP P1 device driver Jungo Lin
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

Implement standard V4L2 video driver that utilizes v4l2
and media frameworks. In this driver, supports one sub-device
and six video devices. Moreover, it also connects with sensor
& senif drivers with camera IO connection via media controller APIs.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 drivers/media/platform/mtk-isp/isp_50/cam/Makefile |   19 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h      |  116 ++
 .../mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c      |  302 +++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.c      |  525 +++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.h      |  166 +++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c         | 1182 ++++++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h         |   43 +
 7 files changed, 2353 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
new file mode 100644
index 0000000..8ddc34b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+mtk-cam-isp-objs += \
+	mtk_cam.o mtk_cam-dev.o mtk_cam-dev-ctx-core.o \
+	mtk_cam-ctrl.o mtk_cam-scp.o \
+	mtk_cam-v4l2-util.o mtk_cam-smem-drv.o
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1_SUPPORT) += mtk-cam-isp.o
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h
new file mode 100644
index 0000000..5f3b807
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctx.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_CTX_H__
+#define __MTK_CAM_CTX_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-subdev.h>
+
+#define MTK_CAM_DEV_NODES			11
+#define MTK_CAM_DEV_FRAME_BUNDLE_BUFFER_MAX	MTK_CAM_DEV_NODES
+
+struct mtk_cam_dev;
+
+/*
+ * struct mtk_cam_dev_node_desc - node attributes
+ *
+ * @id:		 id of the context queue
+ * @name:	 media entity name
+ * @description: descritpion of node
+ * @cap:	 mapped to V4L2 capabilities
+ * @buf_type:	 mapped to V4L2 buffer type
+ * @dma_port:	 the dma port associated to the buffer
+ * @link_flags:	 default media link flags
+ * @smem_alloc:	 using the cam_smem_drv as alloc ctx or not
+ * @capture:	 true for capture queue (device to user)
+ *		 false for output queue (from user to device)
+ * @image:	 true for image node, false for meta node
+ * @num_fmts:	 the number of supported formats
+ * @default_fmt_idx: default format of this node
+ * @max_buf_count: maximum V4L2 buffer count
+ * @ioctl_ops:  mapped to v4l2_ioctl_ops
+ * @fmts:	supported format
+ *
+ */
+struct mtk_cam_dev_node_desc {
+	u8 id;
+	char *name;
+	char *description;
+	u32 cap;
+	u32 buf_type;
+	u32 dma_port;
+	u32 link_flags;
+	u8 smem_alloc:1;
+	u8 capture:1;
+	u8 image:1;
+	u8 num_fmts;
+	u8 default_fmt_idx;
+	u8 max_buf_count;
+	const struct v4l2_ioctl_ops *ioctl_ops;
+	struct v4l2_format *fmts;
+};
+
+/* Attributes setup by device owner */
+struct mtk_cam_dev_queues_setting {
+	struct mtk_cam_dev_node_desc *output_node_descs;
+	unsigned int total_output_nodes;
+	struct mtk_cam_dev_node_desc *capture_node_descs;
+	unsigned int total_capture_nodes;
+};
+
+struct mtk_cam_dev_start_param {
+	int request_fd;
+	struct mtk_cam_dev_buffer*
+		buffers[MTK_CAM_DEV_FRAME_BUNDLE_BUFFER_MAX];
+};
+
+struct mtk_cam_dev_finish_param {
+	int request_fd;
+	unsigned int frame_seq_no;
+	unsigned int state;
+	struct list_head *list_buf;
+};
+
+/* For v4l2 event data, must smaller than 64 bytes */
+struct mtk_cam_dev_stat_event_data {
+	__u32 frame_seq_no;
+	__u32 irq_status_mask;
+	__u32 dma_status_mask;
+};
+
+int mtk_cam_dev_core_queue_setup(struct mtk_cam_dev *cam_dev,
+				 struct mtk_cam_dev_queues_setting *setting);
+int mtk_cam_dev_core_job_finish(struct mtk_cam_dev *cam_dev,
+				struct mtk_cam_dev_finish_param *param);
+int mtk_cam_dev_queue_event_dev_state(struct mtk_cam_dev *cam_dev,
+				      struct mtk_cam_dev_stat_event_data *stat);
+void mtk_cam_dev_fmt_set_img(struct device *dev,
+			     struct v4l2_pix_format_mplane *dest_fmt,
+			     struct v4l2_pix_format_mplane *src_fmt,
+			     unsigned int node_id);
+struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *queue_desc, u32 format);
+void mtk_cam_dev_load_default_fmt(struct device *dev,
+				  struct mtk_cam_dev_node_desc *queue,
+				  struct v4l2_format *dest_fmt);
+void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
+				    struct v4l2_pix_format_mplane *dest_fmt,
+				    unsigned int node_id);
+#endif /*__MTK_CAM_CTX_H__*/
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c
new file mode 100644
index 0000000..c17b294
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev-ctx-core.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-event.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-ctx.h"
+#include "mtk_cam-dev.h"
+#include "mtk_cam-v4l2-util.h"
+#include "mtk_cam-smem.h"
+
+static __u32 get_pixel_byte_by_fmt(__u32 pix_fmt)
+{
+	switch (pix_fmt) {
+	case V4L2_PIX_FMT_MTISP_B8:
+	case V4L2_PIX_FMT_MTISP_F8:
+		return 8;
+	case V4L2_PIX_FMT_MTISP_B10:
+	case V4L2_PIX_FMT_MTISP_F10:
+		return 10;
+	case V4L2_PIX_FMT_MTISP_B12:
+	case V4L2_PIX_FMT_MTISP_F12:
+		return 12;
+	case V4L2_PIX_FMT_MTISP_B14:
+	case V4L2_PIX_FMT_MTISP_F14:
+		return 14;
+	case V4L2_PIX_FMT_MTISP_U8:
+	case V4L2_PIX_FMT_MTISP_U10:
+	case V4L2_PIX_FMT_MTISP_U12:
+	case V4L2_PIX_FMT_MTISP_U14:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static __u32 align_main_stream_size(__u32 size, unsigned int pix_mode)
+{
+	switch (pix_mode) {
+	case default_pixel_mode:
+	case four_pixel_mode:
+		return ALIGN(size, 8);
+	case two_pixel_mode:
+		return ALIGN(size, 4);
+	case one_pixel_mode:
+		return ALIGN(size, 2);
+	default:
+		break;
+	}
+	return 0;
+}
+
+static unsigned int align_packetd_out_size(__u32 size,
+					   unsigned int pix_mode,
+					   __u32 fmt)
+{
+	switch (pix_mode) {
+	case default_pixel_mode:
+	case four_pixel_mode:
+		return ALIGN(size, 16);
+	case two_pixel_mode:
+		return ALIGN(size, 8);
+	case one_pixel_mode:
+		if (fmt == V4L2_PIX_FMT_MTISP_F10)
+			return ALIGN(size, 4);
+		else
+			return ALIGN(size, 8);
+	default:
+		return ALIGN(size, 16);
+	}
+	return 0;
+}
+
+static __u32 cal_main_stream_stride(struct device *dev,
+				    __u32 width,
+				    __u32 pix_fmt,
+				    __u32 pix_mode)
+{
+	__u32 stride;
+	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+	width = ALIGN(width, 4);
+	stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2);
+	/* expand stride, instead of shrink width */
+	stride = align_main_stream_size(stride, pix_mode);
+
+	dev_dbg(dev,
+		"main width:%d, pix_mode:%d, stride:%d\n",
+		width, pix_mode, stride);
+	return stride;
+}
+
+static __u32 cal_packed_out_stride(struct device *dev,
+				   __u32 width,
+				   __u32 pix_fmt,
+				   __u32 pix_mode)
+{
+	__u32 stride;
+	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+	width = ALIGN(width, 4);
+	stride = DIV_ROUND_UP(width * 3, 2);
+	stride = DIV_ROUND_UP(stride * pixel_byte, 8);
+	/* expand stride, instead of shrink width */
+	stride = align_packetd_out_size(stride, pix_mode, pix_fmt);
+
+	dev_dbg(dev,
+		"packed width:%d, pix_mode:%d, stride:%d\n",
+		width, pix_mode, stride);
+
+	return stride;
+}
+
+static __u32 cal_img_stride(struct device *dev,
+			    int node_id,
+			    __u32 width,
+			    __u32 pix_fmt)
+{
+	/* Currently, only support one_pixel_mode */
+	if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT)
+		return cal_main_stream_stride(dev, width, pix_fmt,
+					      one_pixel_mode);
+	else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT)
+		return cal_packed_out_stride(dev, width, pix_fmt,
+					      one_pixel_mode);
+
+	return 0;
+}
+
+struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
+{
+	unsigned int i;
+	struct v4l2_format *dev_fmt;
+
+	for (i = 0; i < desc->num_fmts; i++) {
+		dev_fmt = &desc->fmts[i];
+		if (dev_fmt->fmt.pix_mp.pixelformat == format)
+			return dev_fmt;
+	}
+
+	return NULL;
+}
+
+/* The helper to configure the device context */
+int mtk_cam_dev_core_queue_setup(struct mtk_cam_dev *cam_dev,
+				 struct mtk_cam_dev_queues_setting *setting)
+{
+	unsigned int i, node_idx;
+
+	node_idx = 0;
+
+	/* Setup the output queue */
+	for (i = 0; i < setting->total_output_nodes; i++)
+		cam_dev->mem2mem2_nodes[node_idx++].desc =
+			setting->output_node_descs[i];
+
+	/* Setup the capture queue */
+	for (i = 0; i < setting->total_capture_nodes; i++)
+		cam_dev->mem2mem2_nodes[node_idx++].desc =
+			setting->capture_node_descs[i];
+
+	cam_dev->dev_node_num = node_idx;
+
+	return 0;
+}
+
+int mtk_cam_dev_core_job_finish(struct mtk_cam_dev *cam_dev,
+				struct mtk_cam_dev_finish_param *fram_param)
+{
+	struct mtk_cam_dev_buffer *buf, *b0;
+
+	if (!cam_dev->streaming)
+		return 0;
+
+	dev_dbg(&cam_dev->pdev->dev,
+		"job recvied request fd(%d), frame_seq(%d) state(%d)\n",
+		fram_param->request_fd,
+		fram_param->frame_seq_no,
+		fram_param->state);
+
+	/*
+	 * Set the buffer's VB2 status so that the user can dequeue
+	 * the buffer.
+	 */
+	list_for_each_entry_safe(buf, b0, fram_param->list_buf, list) {
+		list_del(&buf->list);
+		buf->vbb.vb2_buf.timestamp = ktime_get_ns();
+		buf->vbb.sequence = fram_param->frame_seq_no;
+		vb2_buffer_done(&buf->vbb.vb2_buf, fram_param->state);
+	}
+
+	return 0;
+}
+
+int mtk_cam_dev_queue_event_dev_state(struct mtk_cam_dev *cam_dev,
+				      struct mtk_cam_dev_stat_event_data *stat)
+{
+	struct v4l2_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = V4L2_EVENT_FRAME_SYNC;
+	event.u.frame_sync.frame_sequence = stat->frame_seq_no;
+	v4l2_event_queue(cam_dev->subdev.devnode, &event);
+
+	return 0;
+}
+
+/* Calcuate mplane pix format */
+void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
+				    struct v4l2_pix_format_mplane *dest_fmt,
+				    unsigned int node_id)
+{
+	unsigned int i;
+	__u32 bpl, sizeimage, imagsize;
+
+	imagsize = 0;
+	for (i = 0 ; i < dest_fmt->num_planes; ++i) {
+		bpl = cal_img_stride(dev,
+				     node_id,
+				     dest_fmt->width,
+				     dest_fmt->pixelformat);
+		sizeimage = bpl * dest_fmt->height;
+		imagsize += sizeimage;
+		dest_fmt->plane_fmt[i].bytesperline = bpl;
+		dest_fmt->plane_fmt[i].sizeimage = sizeimage;
+		memset(dest_fmt->plane_fmt[i].reserved,
+		       0, sizeof(dest_fmt->plane_fmt[i].reserved));
+		dev_dbg(dev, "plane:%d,bpl:%d,sizeimage:%u\n",
+			i,  bpl, dest_fmt->plane_fmt[i].sizeimage);
+	}
+	if (dest_fmt->num_planes == 1)
+		dest_fmt->plane_fmt[0].sizeimage = imagsize;
+}
+
+void mtk_cam_dev_fmt_set_img(struct device *dev,
+			     struct v4l2_pix_format_mplane *dest_fmt,
+			     struct v4l2_pix_format_mplane *src_fmt,
+			     unsigned int node_id)
+{
+	dest_fmt->width = src_fmt->width;
+	dest_fmt->height = src_fmt->height;
+	dest_fmt->pixelformat = src_fmt->pixelformat;
+	dest_fmt->field = src_fmt->field;
+	dest_fmt->colorspace = src_fmt->colorspace;
+	dest_fmt->num_planes = src_fmt->num_planes;
+	/* Use default */
+	dest_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	dest_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+	dest_fmt->xfer_func =
+		V4L2_MAP_XFER_FUNC_DEFAULT(dest_fmt->colorspace);
+	memset(dest_fmt->reserved, 0, sizeof(dest_fmt->reserved));
+
+	dev_dbg(dev, "%s: Dest Fmt:%c%c%c%c, w*h:%d*%d\n",
+		__func__,
+		(dest_fmt->pixelformat & 0xFF),
+		(dest_fmt->pixelformat >> 8) & 0xFF,
+		(dest_fmt->pixelformat >> 16) & 0xFF,
+		(dest_fmt->pixelformat >> 24) & 0xFF,
+		dest_fmt->width,
+		dest_fmt->height);
+
+	mtk_cam_dev_cal_mplane_pix_fmt(dev, dest_fmt, node_id);
+}
+
+/* Get the default format setting */
+void mtk_cam_dev_load_default_fmt(struct device *dev,
+				  struct mtk_cam_dev_node_desc *queue_desc,
+				  struct v4l2_format *dest)
+{
+	struct v4l2_format *default_fmt =
+		&queue_desc->fmts[queue_desc->default_fmt_idx];
+
+	dest->type = queue_desc->buf_type;
+
+	/* Configure default format based on node type */
+	if (queue_desc->image) {
+		mtk_cam_dev_fmt_set_img(dev,
+					&dest->fmt.pix_mp,
+					&default_fmt->fmt.pix_mp,
+					queue_desc->id);
+	} else {
+		dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
+		dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
+	}
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
new file mode 100644
index 0000000..35e77ee
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+#include "mtk_cam.h"
+#include "mtk_cam-dev.h"
+#include "mtk_cam-smem.h"
+#include "mtk_cam-v4l2-util.h"
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
+	.vidioc_enum_fmt_vid_cap_mplane = mtk_cam_videoc_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = mtk_cam_videoc_g_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = mtk_cam_videoc_s_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = mtk_cam_videoc_try_fmt,
+	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
+	.vidioc_g_input = mtk_cam_vidioc_g_input,
+	.vidioc_s_input = mtk_cam_vidioc_s_input,
+	/* buffer queue management */
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_subscribe_event = mtk_cam_vidioc_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vout_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
+	.vidioc_enum_fmt_vid_out_mplane = mtk_cam_videoc_enum_fmt,
+	.vidioc_g_fmt_vid_out_mplane = mtk_cam_videoc_g_fmt,
+	.vidioc_s_fmt_vid_out_mplane = mtk_cam_videoc_s_fmt,
+	.vidioc_try_fmt_vid_out_mplane = mtk_cam_videoc_try_fmt,
+	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
+	.vidioc_g_input = mtk_cam_vidioc_g_input,
+	.vidioc_s_input = mtk_cam_vidioc_s_input,
+	/* buffer queue management */
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static  const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_fmt_meta_cap = mtk_cam_meta_enum_format,
+	.vidioc_g_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_s_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_try_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static  const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_fmt_meta_out = mtk_cam_meta_enum_format,
+	.vidioc_g_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_s_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_try_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static struct v4l2_format meta_fmts[] = {
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
+			.buffersize = 128 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_3A,
+			.buffersize = 300 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_AF,
+			.buffersize = 160 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_LCS,
+			.buffersize = 80 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_LMV,
+			.buffersize = 128 * PAGE_SIZE,
+		},
+	},
+};
+
+/* Need to update mtk_cam_dev_fmt_set_img for default format configuration */
+static struct v4l2_format stream_out_fmts[] = {
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B8,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B12,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B14,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+};
+
+static struct v4l2_format bin_out_fmts[] = {
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F8,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F12,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F14,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+};
+
+static struct mtk_cam_dev_node_desc
+	output_queues[MTK_CAM_P1_TOTAL_OUTPUT] = {
+	{
+		.id = MTK_CAM_P1_META_IN_0,
+		.name = "meta input",
+		.description = "ISP tuning parameters",
+		.cap = V4L2_CAP_META_OUTPUT,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.link_flags = 0,
+		.capture = false,
+		.image = false,
+		.smem_alloc = true,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 0,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
+	},
+};
+
+static struct mtk_cam_dev_node_desc
+	capture_queues[MTK_CAM_P1_TOTAL_CAPTURE] = {
+	{
+		.id = MTK_CAM_P1_MAIN_STREAM_OUT,
+		.name = "main stream",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = 0,
+		.capture = true,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = R_IMGO,
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_PACKED_BIN_OUT,
+		.name = "packed out",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = 0,
+		.capture = true,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = R_RRZO,
+		.fmts = bin_out_fmts,
+		.num_fmts = ARRAY_SIZE(bin_out_fmts),
+		.default_fmt_idx = 1,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_0,
+		.name = "partial meta 0",
+		.description = "AE/AWB histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = 0,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_AAO | R_FLKO | R_PSO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 1,
+		.max_buf_count = 5,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_1,
+		.name = "partial meta 1",
+		.description = "AF histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = 0,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_AFO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 2,
+		.max_buf_count = 5,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_2,
+		.name = "partial meta 2",
+		.description = "Local contrast enhanced statistics",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_DYNAMIC,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_LCSO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 3,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_3,
+		.name = "partial meta 3",
+		.description = "Local motion vector histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_DYNAMIC,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_LMVO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 4,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+};
+
+static struct mtk_cam_dev_queues_setting queues_setting = {
+	.output_node_descs = output_queues,
+	.total_output_nodes = MTK_CAM_P1_TOTAL_OUTPUT,
+	.capture_node_descs = capture_queues,
+	.total_capture_nodes = MTK_CAM_P1_TOTAL_CAPTURE,
+};
+
+static struct platform_device *
+mtk_cam_dev_of_find_smem_dev(struct platform_device *pdev)
+{
+	struct device_node *smem_dev_node;
+
+	smem_dev_node = of_parse_phandle(pdev->dev.of_node,
+					 "smem_device", 0);
+	if (!smem_dev_node) {
+		dev_err(&pdev->dev,
+			"failed to find isp smem device for (%s)\n",
+			pdev->name);
+		return NULL;
+	}
+
+	dev_dbg(&pdev->dev, "smem of node found, try to discovery device\n");
+	return of_find_device_by_node(smem_dev_node);
+}
+
+/* Initliaze a mtk_cam_dev representing a completed HW ISP device. */
+static int mtk_cam_dev_init(struct mtk_cam_dev *cam_dev,
+			    struct platform_device *pdev)
+{
+	int ret;
+
+	/* v4l2 sub-device registration */
+	dev_info(&cam_dev->pdev->dev, "mem2mem2.name: %s\n",
+		 MTK_CAM_DEV_P1_NAME);
+	ret = mtk_cam_mem2mem2_v4l2_register(cam_dev);
+	if (ret) {
+		dev_err(&cam_dev->pdev->dev,
+			"failed to create V4L2 devices (%d)\n", ret);
+		goto failed_mem2mem2;
+	}
+
+	ret = mtk_cam_v4l2_async_register(cam_dev);
+	if (ret) {
+		dev_err(&cam_dev->pdev->dev, "v4l2 async init failed\n");
+		goto failed_async;
+	}
+
+	return 0;
+
+failed_mem2mem2:
+failed_async:
+	return ret;
+}
+
+/* Get a free buffer from a video node */
+static struct mtk_cam_dev_buffer *
+mtk_cam_dev_get_pending_buf(struct mtk_cam_dev *cam_dev, int node)
+{
+	struct mtk_cam_dev_buffer *buf;
+	struct mtk_cam_video_device *vdev;
+
+	if (node > cam_dev->dev_node_num || node < 0) {
+		dev_err(&cam_dev->pdev->dev, "Invalid mtk_cam_dev node.\n");
+		return NULL;
+	}
+	vdev = &cam_dev->mem2mem2_nodes[node];
+
+	spin_lock(&cam_dev->mem2mem2_nodes[node].slock);
+	buf = list_first_entry_or_null(&vdev->pending_list,
+				       struct mtk_cam_dev_buffer,
+				       list);
+	if (!buf) {
+		spin_unlock(&cam_dev->mem2mem2_nodes[node].slock);
+		return NULL;
+	}
+	list_del(&buf->list);
+	spin_unlock(&cam_dev->mem2mem2_nodes[node].slock);
+
+	return buf;
+}
+
+int mtk_cam_dev_queue_buffers(struct mtk_cam_dev *cam_dev)
+{
+	unsigned int node;
+	const int mtk_cam_dev_node_num = cam_dev->dev_node_num;
+	struct device *dev = &cam_dev->pdev->dev;
+	struct mtk_cam_dev_start_param s_param;
+	struct mtk_cam_dev_buffer *buf;
+
+	memset(&s_param, 0, sizeof(struct mtk_cam_dev_start_param));
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (!cam_dev->streaming) {
+		dev_dbg(dev, "%s: stream off, no enqueue\n", __func__);
+		return 0;
+	}
+
+	/* Check all enabled nodes to collect its buffer  */
+	for (node = 0; node < mtk_cam_dev_node_num; node++) {
+		if (!cam_dev->mem2mem2_nodes[node].enabled)
+			continue;
+
+		dev_dbg(dev, "Check node:%d, queue:%d\n",
+			node, cam_dev->mem2mem2_nodes[node].enabled);
+
+		buf = mtk_cam_dev_get_pending_buf(cam_dev, node);
+		if (!buf) {
+			dev_warn(dev, "No available buffer of enabled node %d\n",
+				 node);
+			continue;
+		}
+
+		buf->daddr =
+			vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
+		if (cam_dev->mem2mem2_nodes[node].desc.smem_alloc) {
+			buf->scp_addr = mtk_cam_smem_iova_to_scp_addr
+				(&cam_dev->smem_pdev->dev,
+				buf->daddr);
+		} else {
+			buf->scp_addr = 0;
+		}
+
+		dev_dbg(dev,
+			"Buf: fd:%d idx:%d state:%d daddr:0x%pK scp_addr:0x%pK",
+			buf->vbb.request_fd,
+			buf->vbb.vb2_buf.index,
+			buf->vbb.vb2_buf.state,
+			buf->daddr,
+			buf->scp_addr);
+
+		s_param.buffers[node] = buf;
+		s_param.request_fd = buf->vbb.request_fd;
+	}
+
+	/* Trigger en-queued job to driver */
+	mtk_isp_enqueue(dev, &s_param);
+
+	return 0;
+}
+
+int mtk_cam_dev_core_init(struct platform_device *pdev,
+			  struct mtk_cam_dev *cam_dev)
+{
+	struct platform_device *smem_dev;
+
+	smem_dev = mtk_cam_dev_of_find_smem_dev(pdev);
+	if (!smem_dev) {
+		dev_err(&pdev->dev, "failed to find smem_dev\n");
+		return -EINVAL;
+	}
+
+	cam_dev->pdev = pdev;
+	cam_dev->smem_pdev = smem_dev;
+
+	mtk_cam_dev_core_queue_setup(cam_dev, &queues_setting);
+	mtk_cam_dev_init(cam_dev, pdev);
+
+	return 0;
+}
+
+int mtk_cam_dev_core_release(struct platform_device *pdev,
+			     struct mtk_cam_dev *cam_dev)
+{
+	mtk_cam_v4l2_async_unregister(cam_dev);
+	mtk_cam_v4l2_unregister(cam_dev);
+
+	return 0;
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
new file mode 100644
index 0000000..57c0261
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#ifndef __MTK_CAM_DEV_H__
+#define __MTK_CAM_DEV_H__
+
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "mtk_cam-ctx.h"
+
+#define MTK_CAM_DEV_NODE_MAX			MTK_CAM_DEV_NODES
+#define MTK_CAM_DEV_P1_NAME			"MTK-ISP-P1-V4L2"
+
+#define MTK_CAM_MAX_SENSORS_NUM		2
+
+/* Sensor index */
+#define MTK_CAM_MAIN_SENSOR		0
+#define MTK_CAM_SUB_SENSOR		1
+
+/* Input video nodes */
+#define MTK_CAM_P1_META_IN_0		0
+#define MTK_CAM_P1_TOTAL_OUTPUT		1
+
+/* Output video nodes */
+#define MTK_CAM_P1_MAIN_STREAM_OUT	1
+#define MTK_CAM_P1_PACKED_BIN_OUT	2
+#define MTK_CAM_P1_META_OUT_0		3
+#define MTK_CAM_P1_META_OUT_1		4
+#define MTK_CAM_P1_META_OUT_2		5
+#define MTK_CAM_P1_META_OUT_3		6
+#define MTK_CAM_P1_TOTAL_CAPTURE	6
+
+struct mtk_cam_dev_buffer {
+	struct vb2_v4l2_buffer	vbb;
+	struct list_head	list;
+	/* Intenal part */
+	dma_addr_t		daddr;
+	dma_addr_t		scp_addr;
+};
+
+/*
+ * struct mtk_cam_video_device - Mediatek video device structure.
+ *
+ * @enabled: indicate stream on or off
+ * @id: id for mtk_cam_dev_node_desc or mem2mem2_nodes array
+ * @vdev_fmt: the V4L2 format of video device
+ * @vdev_apd: the media pad graph object of video device
+ * @vbq: a videobuf queue of video device
+ * @ctrl_handler: the control handler of video device
+ * @desc: the node attributes of video device
+ * @pending_list: list for pending buffers before enqueuing into driver
+ * @lock: serializes vb2 queue and video device operations.
+ * @slock: protect for pending_list.
+ *
+ */
+struct mtk_cam_video_device {
+	unsigned int enabled;
+	unsigned int id;
+	struct v4l2_format vdev_fmt;
+	struct video_device vdev;
+	struct media_pad vdev_pad;
+	struct vb2_queue vbq;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct mtk_cam_dev_node_desc desc;
+	struct list_head pending_list;
+	/* Used for vbq & vdev */
+	struct mutex lock;
+	/* protect for pending_list */
+	spinlock_t slock;
+};
+
+/*
+ * struct mtk_cam_dev - Mediatek camera device structure.
+ *
+ * @cio_enabled: indicate stream on or off
+ * @sensor: sensor sub-device
+ * @seninf: sensor_if sub-device
+ *
+ * Below is the graph topology for Camera IO connection.
+ * sensor 1 (main) --> sensor IF --> P1 sub-device
+ * sensor 2 (sub)  -->
+ *
+ */
+struct mtk_cam_dev {
+	struct platform_device *pdev;
+	struct platform_device *smem_pdev;
+	struct media_pipeline pipeline;
+	struct media_device media_dev;
+	struct media_pad *subdev_pads;
+	struct v4l2_subdev subdev;
+	struct v4l2_device v4l2_dev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_async_notifier notifier;
+	struct mtk_cam_video_device mem2mem2_nodes[MTK_CAM_DEV_NODE_MAX];
+	struct v4l2_subdev *sensor;
+	struct v4l2_subdev *sensor_if;
+	unsigned int cio_enabled;
+	unsigned int cio_pad_sink;
+	unsigned int dev_node_num;
+	unsigned int streaming;
+	int request_fd;
+	unsigned int request_count;
+};
+
+int mtk_cam_dev_core_init(struct platform_device *pdev,
+			  struct mtk_cam_dev *cam_dev);
+int mtk_cam_v4l2_register(struct device *dev,
+			  struct media_device *media_dev,
+			  struct v4l2_device *v4l2_dev,
+			  struct v4l2_ctrl_handler *ctrl_handler);
+int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev);
+int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev);
+int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev);
+void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev);
+int mtk_cam_dev_queue_buffers(struct mtk_cam_dev *cam_dev);
+int mtk_cam_dev_core_release(struct platform_device *pdev,
+			     struct mtk_cam_dev *cam_dev);
+
+static inline struct mtk_cam_video_device *
+file_to_mtk_cam_node(struct file *__file)
+{
+	return container_of(video_devdata(__file),
+		struct mtk_cam_video_device, vdev);
+}
+
+static inline struct mtk_cam_dev *
+mtk_cam_subdev_to_dev(struct v4l2_subdev *__sd)
+{
+	return container_of(__sd,
+		struct mtk_cam_dev, subdev);
+}
+
+static inline struct mtk_cam_video_device *
+mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
+{
+	return container_of(__vq,
+		struct mtk_cam_video_device, vbq);
+}
+
+static inline struct mtk_cam_dev_buffer *
+mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
+{
+	return container_of(__vb,
+		struct mtk_cam_dev_buffer, vbb.vb2_buf);
+}
+
+#endif /* __MTK_CAM_DEV_H__ */
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
new file mode 100644
index 0000000..66738ad
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
@@ -0,0 +1,1182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-v4l2 is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-common.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam-dev.h"
+#include "mtk_cam-v4l2-util.h"
+
+#define MTK_CAM_SENSOR_MAIN_PAD_SRC		0
+#define MTK_CAM_SENSOR_SUB_PAD_SRC		0
+#define MTK_CAM_SENSOR_IF_PAD_MAIN_SINK		0
+#define MTK_CAM_SENSOR_IF_PAD_SUB_SINK		1
+#define MTK_CAM_SENSOR_IF_PAD_SRC		4
+
+#define SUBDEV_CIO_NAME				"cam-io"
+#define SUBDEV_SENINF_NAME			"seninf"
+#define SUBDEV_SENSOR_NAME			"sensor"
+#define SUBDEV_SENSOR_MAIN_NAME			"sensor_main"
+#define SUBDEV_SENSOR_SUB_NAME			"sensor_sub"
+
+static int mtk_cam_subdev_open(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_fh *fh)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	cam_dev->request_fd = -1;
+	cam_dev->request_count = 0;
+
+	return mtk_isp_open(&cam_dev->pdev->dev);
+}
+
+static int mtk_cam_subdev_close(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	return mtk_isp_release(&cam_dev->pdev->dev);
+}
+
+static int mtk_cam_v4l2_discover_sensor(struct mtk_cam_dev *cam_dev)
+{
+	struct media_graph graph;
+	struct media_entity *entity = &cam_dev->subdev.entity;
+	struct media_device *mdev = entity->graph_obj.mdev;
+	struct device *dev = &cam_dev->pdev->dev;
+	struct v4l2_subdev *sensor;
+	struct v4l2_subdev *sensor_if;
+	int ret;
+
+	mutex_lock(&mdev->graph_mutex);
+	ret = media_graph_walk_init(&graph, mdev);
+	if (ret) {
+		mutex_unlock(&mdev->graph_mutex);
+		return ret;
+	}
+
+	sensor = NULL;
+	sensor_if = NULL;
+
+	media_graph_walk_start(&graph, entity);
+	while ((entity = media_graph_walk_next(&graph))) {
+		dev_dbg(dev, "Graph traversal: entity: %s\n", entity->name);
+
+		if (!strcmp(entity->name, SUBDEV_SENINF_NAME)) {
+			sensor_if = media_entity_to_v4l2_subdev(entity);
+			dev_dbg(dev, "Sensor if entity found: %s\n",
+				entity->name);
+		}
+
+		if (!strncmp(entity->name, SUBDEV_SENSOR_NAME, 6)) {
+			sensor = media_entity_to_v4l2_subdev(entity);
+			dev_dbg(dev, "Sensor if entity found: %s\n",
+				entity->name);
+		}
+	}
+	mutex_unlock(&mdev->graph_mutex);
+	media_graph_walk_cleanup(&graph);
+
+	if (!sensor_if) {
+		dev_err(dev, "Sensor IF has not been connected\n");
+		return -EINVAL;
+	}
+	if (!sensor) {
+		dev_err(dev, "Sensor has not been not connected\n");
+		return -EINVAL;
+	}
+	cam_dev->sensor_if = sensor_if;
+	cam_dev->sensor = sensor;
+
+	return 0;
+}
+
+static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	int ret;
+
+	/* Align vb2_core_streamon design */
+	if (cam_dev->streaming) {
+		dev_warn(dev, "already streaming\n", dev);
+		return 0;
+	}
+
+	/*
+	 * Get sensor interace and sensor sub device.
+	 * If the call succeeds, sensor if and sensor are filled
+	 * in isp_dev->cio->sensor_if and isp_dev->cio->sensor.
+	 */
+	ret = mtk_cam_v4l2_discover_sensor(cam_dev);
+	if (ret) {
+		dev_err(dev, "no sensor/sensor if connected:%d\n", ret);
+		return -EPERM;
+	}
+
+	/* seninf must stream on first */
+	dev_dbg(dev, "streamed on sensor-if:%s\n",
+		cam_dev->sensor_if->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor_if, video, s_stream, 1);
+	if (ret) {
+		dev_err(dev, "sensor-if:%s stream on failed:%d\n",
+			cam_dev->sensor_if->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev, "streamed on sensor:%s\n",
+		cam_dev->sensor->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 1);
+	if (ret) {
+		dev_err(dev, "sensor:%s stream on failed:%d\n",
+			cam_dev->sensor->entity.name, ret);
+		goto fail_sensor_on;
+	}
+
+	ret = mtk_isp_streamon(dev);
+	if (ret) {
+		dev_err(dev, "Pass 1 stream on failed:%d\n", ret);
+		goto fail_cam_on;
+	}
+	cam_dev->streaming = true;
+
+	dev_dbg(dev, "streamed on Pass 1\n");
+	mtk_cam_dev_queue_buffers(cam_dev);
+
+	return 0;
+
+fail_cam_on:
+	v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0);
+fail_sensor_on:
+	v4l2_subdev_call(cam_dev->sensor_if, video, s_stream, 0);
+	return -EPERM;
+}
+
+static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	int ret;
+
+	if (!cam_dev->streaming) {
+		dev_warn(dev, "already stream off");
+		return 0;
+	}
+
+	dev_dbg(dev, "streamed off sensor:%s\n",
+		cam_dev->sensor->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0);
+	if (ret) {
+		dev_err(dev, "sensor:%s stream off failed:%d\n",
+			cam_dev->sensor->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev, "streamed off sensor-if:%s\n",
+		cam_dev->sensor_if->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor_if, video, s_stream, 0);
+	if (ret) {
+		dev_err(dev, "sensor_if:%s stream off failed:%d\n",
+			cam_dev->sensor_if->entity.name, ret);
+		goto fail_sensor_off;
+	}
+
+	mtk_isp_streamoff(dev);
+	cam_dev->streaming = false;
+	dev_dbg(dev, "streamed off Pass 1\n");
+
+	return 0;
+
+fail_sensor_off:
+	v4l2_subdev_call(cam_dev->sensor_if, video, s_stream, 1);
+	return -EPERM;
+}
+
+static int mtk_cam_subdev_s_stream(struct v4l2_subdev *sd,
+				   int enable)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	if (enable)
+		return mtk_cam_cio_stream_on(cam_dev);
+	else
+		return mtk_cam_cio_stream_off(cam_dev);
+}
+
+static int mtk_cam_subdev_subscribe_event(struct v4l2_subdev *subdev,
+					  struct v4l2_fh *fh,
+					  struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_FRAME_SYNC:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mtk_cam_link_setup(struct media_entity *entity,
+			      const struct media_pad *local,
+	const struct media_pad *remote, u32 flags)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(entity, struct mtk_cam_dev, subdev.entity);
+	u32 pad = local->index;
+
+	dev_dbg(&cam_dev->pdev->dev, "link setup: %d --> %d\n",
+		pad, remote->index);
+
+	if (pad == cam_dev->cio_pad_sink)
+		cam_dev->cio_enabled = !!(flags & MEDIA_LNK_FL_ENABLED);
+	else
+		cam_dev->mem2mem2_nodes[pad].enabled =
+			!!(flags & MEDIA_LNK_FL_ENABLED);
+
+	return 0;
+}
+
+static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct mtk_cam_dev *mtk_cam_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct device *dev = &mtk_cam_dev->pdev->dev;
+	struct mtk_cam_dev_buffer *buf;
+	struct vb2_v4l2_buffer *v4l2_buf;
+
+	dev_dbg(dev, "queue vb2_buf: Node(%s) queue id(%d)\n",
+		node->vdev.name,
+		node->id);
+
+	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	if (mtk_cam_dev->request_fd != v4l2_buf->request_fd) {
+		mtk_cam_dev->request_fd = v4l2_buf->request_fd;
+		mtk_cam_dev->request_count =
+			vb->req_obj.req->num_incomplete_objects;
+		dev_dbg(dev, "init  mtk_cam_dev_buf, fd(%d) count(%d)\n",
+			v4l2_buf->request_fd,
+			vb->req_obj.req->num_incomplete_objects);
+	}
+
+	/* Added the buffer into the tracking list */
+	spin_lock(&node->slock);
+	list_add_tail(&buf->list, &node->pending_list);
+	spin_unlock(&node->slock);
+
+	mtk_cam_dev->request_count--;
+
+	if (!mtk_cam_dev->request_count) {
+		mtk_cam_dev->request_fd = -1;
+		dev_dbg(dev, "%s: mtk_cam_dev_queue_buffers\n",
+			node->vdev.name);
+		mtk_cam_dev_queue_buffers(mtk_cam_dev);
+	}
+}
+
+static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
+				   unsigned int *num_buffers,
+				   unsigned int *num_planes,
+				   unsigned int sizes[],
+				   struct device *alloc_devs[])
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	struct device *dev = &cam_dev->pdev->dev;
+	unsigned int max_buffer_count = node->desc.max_buf_count;
+	const struct v4l2_format *fmt = &node->vdev_fmt;
+	unsigned int size;
+
+	/* Check the limitation of buffer size */
+	if (max_buffer_count > 0)
+		*num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
+	else
+		*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
+
+	if (node->desc.smem_alloc) {
+		alloc_devs[0] = &cam_dev->smem_pdev->dev;
+		dev_dbg(dev, "Select smem alloc_devs(0x%pK)\n", alloc_devs[0]);
+	} else {
+		alloc_devs[0] = &cam_dev->pdev->dev;
+		dev_dbg(dev, "Select default alloc_devs(0x%pK)\n",
+			alloc_devs[0]);
+	}
+
+	if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
+	    vq->type == V4L2_BUF_TYPE_META_CAPTURE)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	/* Validate initialized num_planes & size[0] */
+	if (*num_planes) {
+		if (sizes[0] < size)
+			return -EINVAL;
+	} else {
+		*num_planes = 1;
+		sizes[0] = size;
+	}
+
+	/* Initialize buffer queue & locks */
+	INIT_LIST_HEAD(&node->pending_list);
+	mutex_init(&node->lock);
+	spin_lock_init(&node->slock);
+
+	return 0;
+}
+
+static bool
+mtk_cam_all_nodes_streaming(struct mtk_cam_dev *cam_dev,
+			    struct mtk_cam_video_device *except)
+{
+	unsigned int i;
+
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
+
+		if (node == except)
+			continue;
+		if (node->enabled && !vb2_start_streaming_called(&node->vbq))
+			return false;
+	}
+
+	return true;
+}
+
+static void mtk_cam_return_all_buffers(struct mtk_cam_dev *cam_dev,
+				       struct mtk_cam_video_device *node,
+				       enum vb2_buffer_state state)
+{
+	struct mtk_cam_dev_buffer *b, *b0;
+	unsigned int i;
+
+	dev_dbg(&cam_dev->pdev->dev, "%s: node:%s", __func__, node->vdev.name);
+
+	/* Return all buffers */
+	spin_lock(&node->slock);
+	list_for_each_entry_safe(b, b0, &node->pending_list, list) {
+		list_del(&b->list);
+	}
+	spin_unlock(&node->slock);
+
+	for (i = 0; i < node->vbq.num_buffers; ++i)
+		if (node->vbq.bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+			vb2_buffer_done(node->vbq.bufs[i], state);
+}
+
+static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
+				       unsigned int count)
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	int ret;
+
+	if (!node->enabled) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d is not enable\n",
+			node->id);
+		ret = -EINVAL;
+		goto fail_return_bufs;
+	}
+
+	ret = media_pipeline_start(&node->vdev.entity, &cam_dev->pipeline);
+	if (ret < 0) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d %s failed\n",
+			node->id, __func__);
+		goto fail_return_bufs;
+	}
+
+	if (!mtk_cam_all_nodes_streaming(cam_dev, node))
+		return 0;
+
+	/* Start streaming of the whole pipeline now */
+	ret = v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 1);
+	if (ret < 0) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d s_stream failed\n",
+			node->id);
+		goto fail_stop_pipeline;
+	}
+	return 0;
+
+fail_stop_pipeline:
+	media_pipeline_stop(&node->vdev.entity);
+fail_return_bufs:
+	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_QUEUED);
+	return ret;
+}
+
+static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+
+	/* Was this the first node with streaming disabled? */
+	if (mtk_cam_all_nodes_streaming(cam_dev, node)) {
+		/* Yes, really stop streaming now */
+		if (v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 0))
+			dev_err(&cam_dev->pdev->dev,
+				"failed to stop streaming\n");
+	}
+	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_ERROR);
+	media_pipeline_stop(&node->vdev.entity);
+}
+
+int mtk_cam_videoc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+
+	strscpy(cap->driver, MTK_CAM_DEV_P1_NAME, sizeof(cap->driver));
+	strscpy(cap->card, MTK_CAM_DEV_P1_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 dev_name(cam_dev->media_dev.dev));
+
+	return 0;
+}
+
+int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->index > node->desc.num_fmts || f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat;
+	f->flags = 0;
+
+	return 0;
+}
+
+int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->fmt = node->vdev_fmt.fmt;
+
+	return 0;
+}
+
+int mtk_cam_videoc_try_fmt(struct file *file, void *fh,
+			   struct v4l2_format *in_fmt)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+	struct v4l2_format *dev_fmt;
+	__u32  width, height;
+
+	if (in_fmt->type != node->vbq.type)
+		return -EINVAL;
+
+	dev_dbg(&cam_dev->pdev->dev, "%s: fmt:%c%c%c%c, w*h:%u*%u\n",
+		__func__,
+		(in_fmt->fmt.pix_mp.pixelformat & 0xFF),
+		(in_fmt->fmt.pix_mp.pixelformat >> 8) & 0xFF,
+		(in_fmt->fmt.pix_mp.pixelformat >> 16) & 0xFF,
+		(in_fmt->fmt.pix_mp.pixelformat >> 24) & 0xFF,
+		in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height);
+
+	width = in_fmt->fmt.pix_mp.width;
+	height = in_fmt->fmt.pix_mp.height;
+
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc,
+				       in_fmt->fmt.pix_mp.pixelformat);
+	if (dev_fmt) {
+		mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
+					&in_fmt->fmt.pix_mp,
+					&dev_fmt->fmt.pix_mp,
+					node->id);
+	} else {
+		mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
+					     &node->desc,
+					     in_fmt);
+	}
+	in_fmt->fmt.pix_mp.width = clamp_t(u32,
+					   width,
+					   CAM_MIN_WIDTH,
+					   in_fmt->fmt.pix_mp.width);
+	in_fmt->fmt.pix_mp.height = clamp_t(u32,
+					    height,
+					    CAM_MIN_HEIGHT,
+					    in_fmt->fmt.pix_mp.height);
+	mtk_cam_dev_cal_mplane_pix_fmt(&cam_dev->pdev->dev,
+				       &in_fmt->fmt.pix_mp,
+				       node->id);
+
+	return 0;
+}
+
+int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	if (cam_dev->streaming)
+		return -EBUSY;
+
+	/* Get the valid format */
+	mtk_cam_videoc_try_fmt(file, fh, f);
+
+	/* Configure to video device */
+	mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
+				&node->vdev_fmt.fmt.pix_mp,
+				&f->fmt.pix_mp,
+				node->id);
+
+	return 0;
+}
+
+int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
+			      struct v4l2_input *input)
+{
+	if (input->index > 0)
+		return -EINVAL;
+
+	strscpy(input->name, "camera", sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	*input = 0;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	return input == 0 ? 0 : -EINVAL;
+}
+
+int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
+				   const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+int mtk_cam_enum_framesizes(struct file *filp, void *priv,
+			    struct v4l2_frmsizeenum *sizes)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
+	struct v4l2_format *dev_fmt;
+
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
+	if (!dev_fmt || sizes->index)
+		return -EINVAL;
+
+	if (node->id == MTK_CAM_P1_MAIN_STREAM_OUT) {
+		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+		sizes->stepwise.max_width = IMG_MAX_WIDTH;
+		sizes->stepwise.min_width = IMG_MIN_WIDTH;
+		sizes->stepwise.max_height = IMG_MAX_HEIGHT;
+		sizes->stepwise.min_height = IMG_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	} else if (node->id == MTK_CAM_P1_PACKED_BIN_OUT) {
+		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+		sizes->stepwise.max_width = RRZ_MAX_WIDTH;
+		sizes->stepwise.min_width = RRZ_MIN_WIDTH;
+		sizes->stepwise.max_height = RRZ_MAX_HEIGHT;
+		sizes->stepwise.min_height = RRZ_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	}
+
+	return 0;
+}
+
+int mtk_cam_meta_enum_format(struct file *file, void *fh,
+			     struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	/* Each node is dedicated to only one meta format */
+	if (f->index > 0 || f->type != node->vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc.description,
+		sizeof(node->desc.description));
+	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
+
+	return 0;
+}
+
+int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	/* Each node is dedicated to only one meta format */
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->fmt = node->vdev_fmt.fmt;
+
+	return 0;
+}
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops mtk_cam_subdev_internal_ops = {
+	.open = mtk_cam_subdev_open,
+	.close = mtk_cam_subdev_close,
+};
+
+static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = {
+	.subscribe_event = mtk_cam_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = {
+	.s_stream = mtk_cam_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_ops mtk_cam_subdev_ops = {
+	.core = &mtk_cam_subdev_core_ops,
+	.video = &mtk_cam_subdev_video_ops,
+};
+
+static const struct media_entity_operations mtk_cam_media_ops = {
+	.link_setup = mtk_cam_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static void mtk_cam_vb2_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_cam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req,
+				   dev->v4l2_dev.ctrl_handler);
+}
+
+static const struct vb2_ops mtk_cam_vb2_ops = {
+	.buf_queue = mtk_cam_vb2_buf_queue,
+	.queue_setup = mtk_cam_vb2_queue_setup,
+	.start_streaming = mtk_cam_vb2_start_streaming,
+	.stop_streaming = mtk_cam_vb2_stop_streaming,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.buf_request_complete = mtk_cam_vb2_buf_request_complete,
+};
+
+static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
+	.unlocked_ioctl = video_ioctl2,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.poll = vb2_fop_poll,
+	.mmap = vb2_fop_mmap,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = v4l2_compat_ioctl32,
+#endif
+};
+
+/*
+ * Config node's video properties
+ * according to the device context requirement
+ */
+static void mtk_cam_node_to_v4l2(struct mtk_cam_dev *cam_dev,
+				 unsigned int node,
+				 struct video_device *vdev,
+				 struct v4l2_format *f)
+{
+	struct mtk_cam_dev_node_desc *node_desc =
+		&cam_dev->mem2mem2_nodes[node].desc;
+
+	/* set cap/type/ioctl_ops of the video device */
+	vdev->device_caps = V4L2_CAP_STREAMING | node_desc->cap;
+	f->type = node_desc->buf_type;
+	vdev->ioctl_ops = node_desc->ioctl_ops;
+
+	mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
+				     node_desc,
+				     f);
+}
+
+static const struct media_device_ops mtk_cam_media_req_ops = {
+	.req_validate = vb2_request_validate,
+	.req_queue = vb2_request_queue,
+};
+
+static int mtk_cam_media_register(struct device *dev,
+				  struct media_device *media_dev)
+{
+	int ret;
+
+	media_dev->dev = dev;
+	strscpy(media_dev->model, MTK_CAM_DEV_P1_NAME,
+		sizeof(media_dev->model));
+	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+		 "platform:%s", dev_name(dev));
+	media_dev->hw_revision = 0;
+	media_device_init(media_dev);
+	media_dev->ops = &mtk_cam_media_req_ops;
+	dev_info(dev, "Register media device: %s, 0x%pK",
+		 MTK_CAM_DEV_P1_NAME, media_dev);
+
+	ret = media_device_register(media_dev);
+	if (ret) {
+		dev_err(dev, "failed to register media device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_cam_v4l2_register(struct device *dev,
+			  struct media_device *media_dev,
+			  struct v4l2_device *v4l2_dev,
+			  struct v4l2_ctrl_handler *ctrl_handler)
+{
+	int ret;
+
+	/* Set up v4l2 device */
+	v4l2_dev->ctrl_handler = ctrl_handler;
+	v4l2_dev->mdev = media_dev;
+	dev_info(dev, "Register v4l2 device: 0x%pK", v4l2_dev);
+	ret = v4l2_device_register(dev, v4l2_dev);
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	unsigned int num_nodes = cam_dev->dev_node_num;
+	/* Total pad numbers is video devices + one seninf pad */
+	unsigned int num_subdev_pads = MTK_CAM_DEV_NODE_MAX + 1;
+	unsigned int i;
+	int ret;
+
+	ret = mtk_cam_media_register(dev,
+				     &cam_dev->media_dev);
+	if (ret) {
+		dev_err(dev, "failed to register media device:%d\n", ret);
+		goto fail_media_dev;
+	}
+
+	ret = mtk_cam_v4l2_register(dev,
+				    &cam_dev->media_dev,
+				    &cam_dev->v4l2_dev,
+				    NULL);
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device:%d\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	/* Initialize subdev media entity */
+	cam_dev->subdev_pads = kcalloc(num_subdev_pads,
+				       sizeof(*cam_dev->subdev_pads),
+				       GFP_KERNEL);
+	if (!cam_dev->subdev_pads) {
+		ret = -ENOMEM;
+		goto fail_subdev_pads;
+	}
+
+	ret = media_entity_pads_init(&cam_dev->subdev.entity,
+				     num_subdev_pads,
+				     cam_dev->subdev_pads);
+	if (ret) {
+		dev_err(dev, "failed initialize media pads:%d:\n", ret);
+		goto fail_media_entity;
+	}
+
+	/* Initialize all pads with MEDIA_PAD_FL_SOURCE */
+	for (i = 0; i < num_subdev_pads; i++)
+		cam_dev->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+	/* Customize connection IO media info. */
+	cam_dev->cio_pad_sink = num_subdev_pads - 1;
+	cam_dev->subdev_pads[cam_dev->cio_pad_sink].flags =
+		MEDIA_PAD_FL_SINK;
+
+	/* Initialize subdev */
+	v4l2_subdev_init(&cam_dev->subdev, &mtk_cam_subdev_ops);
+	cam_dev->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
+	cam_dev->subdev.entity.ops = &mtk_cam_media_ops;
+	cam_dev->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
+				V4L2_SUBDEV_FL_HAS_EVENTS;
+	snprintf(cam_dev->subdev.name, sizeof(cam_dev->subdev.name),
+		 "%s", MTK_CAM_DEV_P1_NAME);
+	v4l2_set_subdevdata(&cam_dev->subdev, cam_dev);
+	cam_dev->subdev.internal_ops = &mtk_cam_subdev_internal_ops;
+
+	dev_info(dev, "register subdev: %s\n", cam_dev->subdev.name);
+	ret = v4l2_device_register_subdev(&cam_dev->v4l2_dev, &cam_dev->subdev);
+	if (ret) {
+		dev_err(dev, "failed initialize subdev:%d\n", ret);
+		goto fail_subdev;
+	}
+
+	/* Create video nodes and links */
+	for (i = 0; i < num_nodes; i++) {
+		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
+		struct video_device *vdev = &node->vdev;
+		struct vb2_queue *vbq = &node->vbq;
+		u32 output = !cam_dev->mem2mem2_nodes[i].desc.capture;
+		u32 link_flags = cam_dev->mem2mem2_nodes[i].desc.link_flags;
+
+		cam_dev->subdev_pads[i].flags = output ?
+			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+		/* Initialize miscellaneous variables */
+		mutex_init(&node->lock);
+		spin_lock_init(&node->slock);
+		INIT_LIST_HEAD(&node->pending_list);
+
+		/* Initialize formats to default values */
+		mtk_cam_node_to_v4l2(cam_dev, i, vdev, &node->vdev_fmt);
+
+		/* Initialize media entities */
+		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
+		if (ret) {
+			dev_err(dev, "failed initialize media pad:%d\n", ret);
+			goto fail_vdev_media_entity;
+		}
+		node->enabled = false;
+		node->id = i;
+		node->vdev_pad.flags = cam_dev->subdev_pads[i].flags;
+		vdev->entity.ops = NULL;
+		/* Initialize vbq */
+		vbq->type = node->vdev_fmt.type;
+		if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT)
+			vbq->io_modes = VB2_MMAP;
+		else
+			vbq->io_modes = VB2_MMAP | VB2_DMABUF;
+		if (vbq->type == V4L2_BUF_TYPE_META_CAPTURE)
+			vdev->entity.function =
+				MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
+		vbq->ops = &mtk_cam_vb2_ops;
+		vbq->mem_ops = &vb2_dma_contig_memops;
+		vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer);
+		vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+		vbq->min_buffers_needed = 0;	/* Can streamon w/o buffers */
+		/* Put the process hub sub device in the vb2 private data */
+		vbq->drv_priv = cam_dev;
+		vbq->lock = &node->lock;
+		vbq->supports_requests = true;
+
+		ret = vb2_queue_init(vbq);
+		if (ret) {
+			dev_err(dev, "failed to init. vb2 queue:%d\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Initialize vdev */
+		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
+			 MTK_CAM_DEV_P1_NAME, node->desc.name);
+		vdev->release = video_device_release_empty;
+		vdev->fops = &mtk_cam_v4l2_fops;
+		vdev->lock = &node->lock;
+		vdev->v4l2_dev = &cam_dev->v4l2_dev;
+		vdev->queue = &node->vbq;
+		vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
+		/* Enable private control for image video devices */
+		if (node->desc.image) {
+			mtk_cam_ctrl_init(cam_dev, &node->ctrl_handler);
+			vdev->ctrl_handler = &node->ctrl_handler;
+		}
+		video_set_drvdata(vdev, cam_dev);
+		dev_info(dev, "register vdev:%d:%s\n", i, vdev->name);
+		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+		if (ret) {
+			dev_err(dev, "failed to register vde:%d\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Create link between video node and the subdev pad */
+		if (output) {
+			ret = media_create_pad_link(&vdev->entity, 0,
+						    &cam_dev->subdev.entity,
+						    i, link_flags);
+		} else {
+			ret = media_create_pad_link(&cam_dev->subdev.entity,
+						    i, &vdev->entity, 0,
+						    link_flags);
+		}
+		if (ret)
+			goto fail_link;
+	}
+
+	return 0;
+
+	for (; i >= 0; i--) {
+fail_link:
+		video_unregister_device(&cam_dev->mem2mem2_nodes[i].vdev);
+fail_vdev:
+		media_entity_cleanup(&cam_dev->mem2mem2_nodes[i].vdev.entity);
+fail_vdev_media_entity:
+		mutex_destroy(&cam_dev->mem2mem2_nodes[i].lock);
+	}
+fail_subdev:
+	media_entity_cleanup(&cam_dev->subdev.entity);
+fail_media_entity:
+	kfree(cam_dev->subdev_pads);
+fail_subdev_pads:
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+fail_v4l2_dev:
+fail_media_dev:
+	dev_err(dev, "fail_v4l2_dev mdev: 0x%pK", &cam_dev->media_dev);
+	media_device_unregister(&cam_dev->media_dev);
+	media_device_cleanup(&cam_dev->media_dev);
+
+	return ret;
+}
+
+int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev)
+{
+	unsigned int i;
+	struct mtk_cam_video_device *dev;
+
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		dev = &cam_dev->mem2mem2_nodes[i];
+		video_unregister_device(&dev->vdev);
+		media_entity_cleanup(&dev->vdev.entity);
+		mutex_destroy(&dev->lock);
+		if (dev->desc.image)
+			v4l2_ctrl_handler_free(&dev->ctrl_handler);
+	}
+
+	v4l2_device_unregister_subdev(&cam_dev->subdev);
+	media_entity_cleanup(&cam_dev->subdev.entity);
+	kfree(cam_dev->subdev_pads);
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+	media_device_unregister(&cam_dev->media_dev);
+	media_device_cleanup(&cam_dev->media_dev);
+
+	return 0;
+}
+
+struct sensor_async_subdev {
+	struct v4l2_async_subdev asd;
+};
+
+static struct v4l2_subdev *get_subdev_by_name(struct mtk_cam_dev *cam_dev,
+					      char *name)
+{
+	struct device_node *node;
+	struct v4l2_subdev *sd;
+
+	list_for_each_entry(sd, &cam_dev->v4l2_dev.subdevs, list) {
+		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+			continue;
+		node = to_of_node(sd->fwnode);
+		if (node) {
+			if (!strcmp(node->name, name))
+				return sd;
+		}
+	}
+	return NULL;
+}
+
+static int mtk_cam_dev_complete(struct v4l2_async_notifier *notifier)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+	struct device *dev = &cam_dev->pdev->dev;
+	struct v4l2_subdev *sd;
+	struct v4l2_subdev *src_sd, *sink_sd;
+	struct device_node *node;
+	int ret;
+
+	dev_info(dev, "Complete the v4l2 registration\n");
+
+	ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "failed initialize subdev nodes:%d\n", ret);
+		return ret;
+	}
+
+	/* Links among sensors, sensor interface and cio */
+	list_for_each_entry(sd, &cam_dev->v4l2_dev.subdevs, list) {
+		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+			continue;
+		node = to_of_node(sd->fwnode);
+		if (node)
+			sd->entity.name = node->name;
+	}
+
+	src_sd = get_subdev_by_name(cam_dev, SUBDEV_SENSOR_MAIN_NAME);
+	sink_sd = get_subdev_by_name(cam_dev, SUBDEV_SENINF_NAME);
+	if (src_sd && sink_sd) {
+		dev_dbg(dev, "Link create:%s-->%s\n",
+			src_sd->entity.name, sink_sd->entity.name);
+		ret = media_create_pad_link(&src_sd->entity,
+					    MTK_CAM_SENSOR_MAIN_PAD_SRC,
+					    &sink_sd->entity,
+					    MTK_CAM_SENSOR_IF_PAD_MAIN_SINK,
+					    0);
+		if (ret)
+			dev_err(dev,
+				"fail to create pad link %s %s, ret:%d\n",
+				src_sd->entity.name, sink_sd->entity.name,
+				ret);
+	} else {
+		dev_err(dev, "not found: sensor_main(0x%pK), seninf(%pK)\n",
+			src_sd, sink_sd);
+	}
+
+	src_sd = get_subdev_by_name(cam_dev, SUBDEV_SENSOR_SUB_NAME);
+	if (src_sd && sink_sd) {
+		dev_dbg(dev, "Link create: %s --> %s\n",
+			src_sd->entity.name, sink_sd->entity.name);
+		ret = media_create_pad_link(&src_sd->entity,
+					    MTK_CAM_SENSOR_SUB_PAD_SRC,
+					    &sink_sd->entity,
+					    MTK_CAM_SENSOR_IF_PAD_SUB_SINK,
+					    0);
+		if (ret)
+			dev_err(dev,
+				"fail to create pad link %s %s, ret:%d:\n",
+				src_sd->entity.name, sink_sd->entity.name,
+				ret);
+	} else {
+		dev_warn(dev, "not found: sensor_sub(0x%pK), seninf(0x%pK)\n",
+			 src_sd, sink_sd);
+	}
+
+	ret = media_create_pad_link(&sink_sd->entity,
+				    MTK_CAM_SENSOR_IF_PAD_SRC,
+				    &cam_dev->subdev.entity,
+				    cam_dev->cio_pad_sink,
+				    0);
+	if (ret)
+		dev_err(dev, "fail to create pad link %s %s err:%d\n",
+			sink_sd->entity.name, cam_dev->subdev.entity.name, ret);
+
+	return ret;
+}
+
+static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier,
+				      struct v4l2_subdev *sd,
+				      struct v4l2_async_subdev *asd)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+	struct device *dev = &cam_dev->pdev->dev;
+
+	dev_info(dev, "%s bound\n", sd->entity.name);
+	if (!strncmp(&sd->entity.name[9],
+		     SUBDEV_SENINF_NAME,
+		     strlen(SUBDEV_SENINF_NAME)))
+		mtk_cam_dev_complete(notifier);
+
+	return 0;
+}
+
+static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier,
+					struct v4l2_subdev *sd,
+					struct v4l2_async_subdev *asd)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+	struct device *dev = &cam_dev->pdev->dev;
+
+	dev_dbg(dev, "%s unbound\n", sd->entity.name);
+}
+
+static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+	return 0;
+}
+
+static const struct v4l2_async_notifier_operations mtk_cam_async_ops = {
+	.bound = mtk_cam_dev_notifier_bound,
+	.unbind = mtk_cam_dev_notifier_unbind,
+	.complete = mtk_cam_dev_notifier_complete,
+};
+
+static int mtk_cam_dev_fwnode_parse(struct device *dev,
+				    struct v4l2_fwnode_endpoint *vep,
+				    struct v4l2_async_subdev *asd)
+{
+	dev_dbg(dev, "%s: To be implemented\n", __func__);
+
+	return 0;
+}
+
+int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev)
+{
+	int ret;
+
+	ret = v4l2_async_notifier_parse_fwnode_endpoints
+		(&cam_dev->pdev->dev, &cam_dev->notifier,
+		 sizeof(struct sensor_async_subdev),
+		 mtk_cam_dev_fwnode_parse);
+	if (ret < 0)
+		return ret;
+
+	if (!cam_dev->notifier.num_subdevs)
+		return -ENODEV;
+
+	cam_dev->notifier.ops = &mtk_cam_async_ops;
+	dev_info(&cam_dev->pdev->dev, "mtk_cam v4l2_async_notifier_register\n");
+	ret = v4l2_async_notifier_register(&cam_dev->v4l2_dev,
+					   &cam_dev->notifier);
+	if (ret) {
+		dev_err(&cam_dev->pdev->dev,
+			"failed to register async notifier : %d\n", ret);
+		v4l2_async_notifier_cleanup(&cam_dev->notifier);
+	}
+
+	return ret;
+}
+
+void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev)
+{
+	v4l2_async_notifier_unregister(&cam_dev->notifier);
+	v4l2_async_notifier_cleanup(&cam_dev->notifier);
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
new file mode 100644
index 0000000..73b3691
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_DEV_V4L2_H__
+#define __MTK_CAM_DEV_V4L2_H__
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+int mtk_cam_videoc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap);
+int mtk_cam_enum_framesizes(struct file *filp, void *priv,
+			    struct v4l2_frmsizeenum *sizes);
+int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f);
+int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int mtk_cam_videoc_try_fmt(struct file *file,
+			   void *fh, struct v4l2_format *in_fmt);
+int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
+			      struct v4l2_input *input);
+int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input);
+int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input);
+int mtk_cam_meta_enum_format(struct file *file, void *fh,
+			     struct v4l2_fmtdesc *f);
+int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f);
+int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
+				   const struct v4l2_event_subscription *sub);
+
+#endif /* __MTK_CAM_DEV_V4L2_H__ */
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 10/12] media: platform: Add Mediatek ISP P1 device driver
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (8 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 09/12] media: platform: Add Mediatek ISP P1 V4L2 functions Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 11/12] media: platform: Add Mediatek ISP P1 SCP communication Jungo Lin
  2019-03-28  9:56   ` [RFC V1 12/12] media: platform: Add Mediatek ISP P1 shared memory driver Jungo Lin
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds the Mediatek ISP P1 HW control device driver.
It handles the ISP HW configuration, provides interrupt handling and
initializes the V4L2 device nodes and other functions.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../platform/mtk-isp/isp_50/cam/mtk_cam-regs.h     |  147 +++
 .../media/platform/mtk-isp/isp_50/cam/mtk_cam.c    | 1098 ++++++++++++++++++++
 .../media/platform/mtk-isp/isp_50/cam/mtk_cam.h    |  288 +++++
 3 files changed, 1533 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
new file mode 100644
index 0000000..90736a1
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_REGS_H
+#define _CAM_REGS_H
+
+/* TG Bit Mask */
+#define VFDATA_EN_BIT	BIT(0)
+#define CMOS_EN_BIT	BIT(0)
+
+/* normal signal bit */
+#define VS_INT_ST	BIT(0)
+#define HW_PASS1_DON_ST	BIT(11)
+#define SOF_INT_ST	BIT(12)
+#define SW_PASS1_DON_ST	BIT(30)
+
+/* err status bit */
+#define TG_ERR_ST	BIT(4)
+#define TG_GBERR_ST	BIT(5)
+#define CQ_CODE_ERR_ST	BIT(6)
+#define CQ_APB_ERR_ST	BIT(7)
+#define CQ_VS_ERR_ST	BIT(8)
+#define AMX_ERR_ST	BIT(15)
+#define RMX_ERR_ST	BIT(16)
+#define BMX_ERR_ST	BIT(17)
+#define RRZO_ERR_ST	BIT(18)
+#define AFO_ERR_ST	BIT(19)
+#define IMGO_ERR_ST	BIT(20)
+#define AAO_ERR_ST	BIT(21)
+#define PSO_ERR_ST	BIT(22)
+#define LCSO_ERR_ST	BIT(23)
+#define BNR_ERR_ST	BIT(24)
+#define LSCI_ERR_ST	BIT(25)
+#define DMA_ERR_ST	BIT(29)
+
+/* CAM DMA done status */
+#define FLKO_DONE_ST	BIT(4)
+#define AFO_DONE_ST	BIT(5)
+#define AAO_DONE_ST	BIT(7)
+#define PSO_DONE_ST	BIT(14)
+
+/* IRQ signal mask */
+#define INT_ST_MASK_CAM	( \
+			VS_INT_ST |\
+			HW_PASS1_DON_ST |\
+			SOF_INT_ST |\
+			SW_PASS1_DON_ST)
+
+/* IRQ Warning Mask */
+#define INT_ST_MASK_CAM_WARN	(\
+				RRZO_ERR_ST |\
+				AFO_ERR_ST |\
+				IMGO_ERR_ST |\
+				AAO_ERR_ST |\
+				PSO_ERR_ST | \
+				LCSO_ERR_ST |\
+				BNR_ERR_ST |\
+				LSCI_ERR_ST)
+
+/* IRQ Error Mask */
+#define INT_ST_MASK_CAM_ERR	(\
+				TG_ERR_ST |\
+				TG_GBERR_ST |\
+				CQ_CODE_ERR_ST |\
+				CQ_APB_ERR_ST |\
+				CQ_VS_ERR_ST |\
+				BNR_ERR_ST |\
+				RMX_ERR_ST |\
+				BMX_ERR_ST |\
+				BNR_ERR_ST |\
+				LSCI_ERR_ST |\
+				DMA_ERR_ST)
+
+/* IRQ Signal Log Mask */
+#define INT_ST_LOG_MASK_CAM	(\
+				SOF_INT_ST |\
+				SW_PASS1_DON_ST |\
+				VS_INT_ST |\
+				TG_ERR_ST |\
+				TG_GBERR_ST |\
+				RRZO_ERR_ST |\
+				AFO_ERR_ST |\
+				IMGO_ERR_ST |\
+				AAO_ERR_ST |\
+				DMA_ERR_ST)
+
+/* DMA Event Notification Mask */
+#define DMA_ST_MASK_CAM	(\
+			AFO_DONE_ST |\
+			AAO_DONE_ST |\
+			PSO_DONE_ST |\
+			FLKO_DONE_ST)
+
+/* Status check */
+#define REG_CTL_EN		0x0004
+#define REG_CTL_DMA_EN		0x0008
+#define REG_CTL_FMT_SEL		0x0010
+#define REG_CTL_EN2		0x0018
+#define REG_CTL_RAW_INT_EN	0x0020
+#define REG_CTL_RAW_INT_STAT	0x0024
+#define REG_CTL_RAW_INT2_STAT	0x0034
+#define REG_CTL_RAW_INT3_STAT	0x00c4
+#define REG_CTL_TWIN_STAT	0x0050
+
+#define REG_TG_SEN_MODE		0x0230
+#define REG_TG_SEN_GRAB_PIX	0x0238
+#define REG_TG_SEN_GRAB_LIN	0x023c
+#define REG_TG_VF_CON		0x0234
+#define REG_TG_INTER_ST		0x026c
+#define REG_TG_SUB_PERIOD	0x02a4
+
+#define REG_IMGO_BASE_ADDR	0x1020
+#define REG_RRZO_BASE_ADDR	0x1050
+
+/* Error status log */
+#define REG_IMGO_ERR_STAT	0x1360
+#define REG_RRZO_ERR_STAT	0x1364
+#define REG_AAO_ERR_STAT	0x1368
+#define REG_AFO_ERR_STAT	0x136c
+#define REG_LCSO_ERR_STAT	0x1370
+#define REG_UFEO_ERR_STAT	0x1374
+#define REG_PDO_ERR_STAT	0x1378
+#define REG_BPCI_ERR_STAT	0x137c
+#define REG_LSCI_ERR_STAT	0x1384
+#define REG_PDI_ERR_STAT	0x138c
+#define REG_LMVO_ERR_STAT	0x1390
+#define REG_FLKO_ERR_STAT	0x1394
+#define REG_PSO_ERR_STAT	0x13a0
+
+/* ISP command */
+#define REG_CQ_THR0_BASEADDR	0x0198
+#define REG_CTL_SPARE2		0x0058
+#define REG_HW_FRAME_NUM	0x13b8
+
+#endif	/* _CAM_REGS_H */
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
new file mode 100644
index 0000000..935cf7e
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
@@ -0,0 +1,1098 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/sched/clock.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-regs.h"
+#include "mtk_cam-ctx.h"
+
+static const struct of_device_id mtk_isp_of_ids[] = {
+	{.compatible = "mediatek,mt8183-camisp",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_isp_of_ids);
+
+/* list of clocks required by isp cam */
+static const char * const mtk_isp_clks[] = {
+	"CAMSYS_CAM_CGPDN", "CAMSYS_CAMTG_CGPDN"
+};
+
+static void isp_dumpdmastat(struct isp_device *isp_dev)
+{
+	dev_err(isp_dev->dev,
+		"IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n",
+		readl(isp_dev->regs + REG_IMGO_ERR_STAT),
+		readl(isp_dev->regs + REG_RRZO_ERR_STAT),
+		readl(isp_dev->regs + REG_AAO_ERR_STAT),
+		readl(isp_dev->regs + REG_AFO_ERR_STAT),
+		readl(isp_dev->regs + REG_LMVO_ERR_STAT));
+	dev_err(isp_dev->dev,
+		"LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n",
+		readl(isp_dev->regs + REG_LCSO_ERR_STAT),
+		readl(isp_dev->regs + REG_PSO_ERR_STAT),
+		readl(isp_dev->regs + REG_FLKO_ERR_STAT),
+		readl(isp_dev->regs + REG_BPCI_ERR_STAT),
+		readl(isp_dev->regs + REG_LSCI_ERR_STAT));
+}
+
+static void mtk_isp_notify(struct mtk_isp_p1_ctx *isp_ctx,
+			   unsigned int request_fd,
+			   unsigned int frame_seq_no,
+			   struct list_head *list_buf,
+			   enum vb2_buffer_state state)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_cam_dev_finish_param fram_param;
+
+	fram_param.list_buf = list_buf;
+	fram_param.request_fd = request_fd;
+	fram_param.frame_seq_no = frame_seq_no;
+	fram_param.state = state;
+	dev_dbg(dev, "request fd(%d) frame_seq_no(%d)\n",
+		fram_param.request_fd,
+		fram_param.frame_seq_no);
+	mtk_cam_dev_core_job_finish(p1_dev->cam_dev, &fram_param);
+}
+
+static void isp_deque_frame(struct isp_p1_device *p1_dev, int frame_seq_no)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct platform_device *pdev = p1_dev->pdev;
+	struct mtk_isp_queue_job *framejob, *tmp;
+	struct isp_queue *p1_enqueue_list = &isp_ctx->p1_enqueue_list;
+
+	/* Match dequeue work and enqueue frame */
+	spin_lock(&p1_enqueue_list->lock);
+	list_for_each_entry_safe(framejob, tmp, &p1_enqueue_list->queue,
+				 list_entry) {
+		dev_dbg(&pdev->dev,
+			"%s frame_seq_no=%d, isp_composer_work->req_num=%d\n",
+			__func__,
+			framejob->frame_seq_no, frame_seq_no);
+		/* Match by the en-queued request number */
+		if (framejob->frame_seq_no == frame_seq_no) {
+			/* Pass to user space */
+			mtk_isp_notify(isp_ctx,
+				       framejob->request_fd,
+				       framejob->frame_seq_no,
+				       &framejob->list_buf,
+				       VB2_BUF_STATE_DONE);
+			atomic_dec(&p1_enqueue_list->queue_cnt);
+			dev_dbg(&pdev->dev,
+				"frame(frame_seq_no=%d) is done, queue_cnt(%d)\n",
+				framejob->frame_seq_no,
+				atomic_read(&p1_enqueue_list->queue_cnt));
+
+			/* remove only when frame ready */
+			list_del(&framejob->list_entry);
+			kfree(framejob);
+			break;
+		} else if (framejob->frame_seq_no < frame_seq_no) {
+			/* Pass to user space for frame drop */
+			mtk_isp_notify(isp_ctx,
+				       framejob->request_fd,
+				       framejob->frame_seq_no,
+				       &framejob->list_buf,
+				       VB2_BUF_STATE_ERROR);
+			atomic_dec(&p1_enqueue_list->queue_cnt);
+			dev_dbg(&pdev->dev,
+				"frame(frame_seq_no=%d) drop, queue_cnt(%d)\n",
+				framejob->frame_seq_no,
+				atomic_read(&p1_enqueue_list->queue_cnt));
+
+			/* remove only drop frame */
+			list_del(&framejob->list_entry);
+			kfree(framejob);
+		}
+	}
+	spin_unlock(&p1_enqueue_list->lock);
+}
+
+static int isp_deque_work(void *data)
+{
+	struct isp_p1_device *p1_dev = (struct isp_p1_device *)data;
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
+	struct mtk_cam_dev_stat_event_data event_data;
+	atomic_t *irq_data_end = &isp_ctx->irq_data_end;
+	atomic_t *irq_data_start = &isp_ctx->irq_data_start;
+	unsigned long flags;
+	int ret, i;
+
+	while (1) {
+		ret = wait_event_interruptible(isp_ctx->isp_deque_thread.wq,
+					       (atomic_read(irq_data_end) !=
+					       atomic_read(irq_data_start)) ||
+					       kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
+		i = atomic_read(&isp_ctx->irq_data_start);
+		memcpy(&event_data, &isp_ctx->irq_event_datas[i],
+		       sizeof(event_data));
+		memset(&isp_ctx->irq_event_datas[i], 0x00, sizeof(event_data));
+		atomic_set(&isp_ctx->irq_data_start, ++i & 0x3);
+		spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
+
+		if (event_data.irq_status_mask & VS_INT_ST) {
+			/* Notify specific HW events to user space */
+			mtk_cam_dev_queue_event_dev_state(cam_dev,
+							  &event_data);
+			dev_dbg(dev,
+				"event IRQ(0x%x) DMA(0x%x) is sent\n",
+				event_data.irq_status_mask,
+				event_data.dma_status_mask);
+			mtk_cam_dev_queue_event_dev_state(cam_dev,
+							  &event_data);
+		} else if (event_data.irq_status_mask & SW_PASS1_DON_ST) {
+			isp_deque_frame(p1_dev,
+					event_data.frame_seq_no);
+		}
+	}
+	return 0;
+}
+
+static int irq_handle_sof(struct isp_device *isp_dev,
+			  dma_addr_t base_addr,
+			  unsigned int frame_num)
+{
+	unsigned int cq_addr_index;
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	int cq_num = atomic_read(&p1_dev->isp_ctx.composed_frame_id);
+
+	if (cq_num > frame_num) {
+		cq_addr_index = frame_num % CQ_BUFFER_COUNT;
+
+		writel(base_addr +
+			(dma_addr_t)(CQ_ADDRESS_OFFSET * cq_addr_index),
+			isp_dev->regs + REG_CQ_THR0_BASEADDR);
+		dev_dbg(isp_dev->dev,
+			"SOF_INT_ST cq_num:%d, frame_num:%d cq_addr:%d",
+			cq_num, frame_num, cq_addr_index);
+	} else {
+		dev_dbg(isp_dev->dev,
+			"SOF_INT_ST cq_num:%d, frame_num:%d",
+			cq_num, frame_num);
+	}
+
+	isp_dev->sof_count += 1;
+
+	return cq_num;
+}
+
+static int irq_handle_notify_event(struct isp_device *isp_dev,
+				   unsigned int irqstatus,
+				   unsigned int dmastatus)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
+	i = atomic_read(&isp_ctx->irq_data_end);
+	isp_ctx->irq_event_datas[i].frame_seq_no = isp_dev->current_frame;
+	isp_ctx->irq_event_datas[i].irq_status_mask |=
+		(irqstatus & INT_ST_MASK_CAM);
+	isp_ctx->irq_event_datas[i].dma_status_mask |=
+		(dmastatus & DMA_ST_MASK_CAM);
+	atomic_set(&isp_ctx->irq_data_end, ++i & 0x3);
+	spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
+
+	wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
+
+	dev_dbg(isp_dev->dev,
+		"%s notify IRQ (0x%x) DMA status (0x%x) for frame_seq_no: %d\n",
+		__func__,
+		(irqstatus & INT_ST_MASK_CAM),
+		(dmastatus & DMA_ST_MASK_CAM),
+		isp_dev->current_frame);
+
+	return 0;
+}
+
+irqreturn_t isp_irq_cam(int irq, void *data)
+{
+	struct isp_device *isp_dev = (struct isp_device *)data;
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = isp_dev->dev;
+	unsigned int cardinalnum, cq_num, hw_frame_num;
+	unsigned int irqstatus, errstatus, warnstatus, dmastatus;
+	unsigned long flags;
+
+	/* Check the streaming is off or not */
+	if (!p1_dev->cam_dev->streaming)
+		return IRQ_HANDLED;
+
+	cardinalnum = isp_dev->isp_hw_module - ISP_CAM_A_IDX;
+	cq_num = 0;
+
+	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
+	irqstatus = readl(isp_dev->regs + REG_CTL_RAW_INT_STAT);
+	dmastatus = readl(isp_dev->regs + REG_CTL_RAW_INT2_STAT);
+	hw_frame_num = readl(isp_dev->regs + REG_HW_FRAME_NUM);
+	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
+
+	/* Ignore unnecessary IRQ */
+	if (irqstatus == 0)
+		return IRQ_HANDLED;
+
+	errstatus = irqstatus & INT_ST_MASK_CAM_ERR;
+	warnstatus = irqstatus & INT_ST_MASK_CAM_WARN;
+	irqstatus = irqstatus & INT_ST_MASK_CAM;
+
+	/* sof , done order check . */
+	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
+	if ((irqstatus & HW_PASS1_DON_ST) && (irqstatus & SOF_INT_ST)) {
+		dev_warn(dev,
+			 "isp sof_don block, %d\n",
+			 isp_dev->sof_count);
+
+		/* Notify IRQ event and enqueue ready frame */
+		irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
+		isp_dev->current_frame = hw_frame_num;
+	} else {
+		if (irqstatus & SOF_INT_ST)
+			isp_dev->current_frame = hw_frame_num;
+
+		if ((irqstatus & INT_ST_MASK_CAM) ||
+		    (dmastatus & DMA_ST_MASK_CAM))
+			irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
+	}
+	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
+
+	if (irqstatus & SOF_INT_ST)
+		cq_num = irq_handle_sof(isp_dev, isp_ctx->scp_mem_iova,
+					hw_frame_num);
+
+	if (irqstatus & SW_PASS1_DON_ST) {
+		int num = atomic_dec_return(&isp_ctx->composing_frame);
+
+		dev_dbg(dev, "SW_PASS1_DON_ST queued frame:%d\n", num);
+		/* Notify TX thread to send if TX frame is blocked */
+		wake_up_interruptible
+				(&isp_ctx->composer_tx_thread.wq);
+	}
+
+	/* check ISP error status */
+	if (errstatus) {
+		dev_err(dev,
+			"raw_int_err:0x%x/0x%x, raw_int3_err:0x%x\n",
+			irqstatus, warnstatus, errstatus);
+
+		/* show DMA errors in detail */
+		if (errstatus & DMA_ERR_ST)
+			isp_dumpdmastat(isp_dev);
+	}
+
+	if (irqstatus & INT_ST_LOG_MASK_CAM)
+		dev_dbg(dev, IRQ_STAT_STR,
+			'A' + cardinalnum,
+			isp_dev->sof_count,
+			irqstatus,
+			dmastatus,
+			hw_frame_num,
+			cq_num);
+	return IRQ_HANDLED;
+}
+
+static int enable_sys_clock(struct isp_p1_device *p1_dev)
+{
+	struct device *dev = &p1_dev->pdev->dev;
+	int ret;
+
+	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
+
+	ret = clk_bulk_prepare_enable(p1_dev->isp_clk.num_clks,
+				      p1_dev->isp_clk.clk_list);
+	if (ret < 0)
+		goto clk_err;
+	return 0;
+clk_err:
+	dev_err(dev, "cannot pre-en isp_cam clock:%d\n", ret);
+	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
+				   p1_dev->isp_clk.clk_list);
+	return ret;
+}
+
+static void disable_sys_clock(struct isp_p1_device *p1_dev)
+{
+	struct device *dev = &p1_dev->pdev->dev;
+
+	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
+	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
+				   p1_dev->isp_clk.clk_list);
+}
+
+static int mtk_isp_probe(struct platform_device *pdev)
+{
+	struct isp_p1_device *p1_dev;
+	struct mtk_isp_p1_ctx *isp_ctx;
+	struct isp_device *isp_dev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+	unsigned int i;
+
+	/* Allocate context */
+	p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL);
+	if (!p1_dev)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, p1_dev);
+	isp_ctx = &p1_dev->isp_ctx;
+	p1_dev->pdev = pdev;
+
+	p1_dev->isp_devs =
+		devm_kzalloc(dev,
+			     sizeof(struct isp_device) * ISP_DEV_NODE_NUM,
+			     GFP_KERNEL);
+	if (!p1_dev->isp_devs)
+		return -ENOMEM;
+
+	p1_dev->cam_dev =
+		devm_kzalloc(dev, sizeof(struct mtk_cam_dev), GFP_KERNEL);
+	if (!p1_dev->isp_devs)
+		return -ENOMEM;
+
+	/* iomap registers */
+	for (i = ISP_CAMSYS_CONFIG_IDX; i < ISP_DEV_NODE_NUM; i++) {
+		isp_dev = &p1_dev->isp_devs[i];
+		isp_dev->isp_hw_module = i;
+		isp_dev->dev = dev;
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		isp_dev->regs = devm_ioremap_resource(dev, res);
+
+		dev_info(dev, "cam%u, map_addr=0x%lx\n",
+			 i, (unsigned long)isp_dev->regs);
+
+		if (!isp_dev->regs)
+			return PTR_ERR(isp_dev->regs);
+
+		/* support IRQ from ISP_CAM_A_IDX */
+		if (i >= ISP_CAM_A_IDX) {
+			/* reg & interrupts index is shifted with 1  */
+			isp_dev->irq = platform_get_irq(pdev, i - 1);
+			if (isp_dev->irq > 0) {
+				ret = devm_request_irq(dev, isp_dev->irq,
+						       isp_irq_cam,
+						       IRQF_SHARED,
+						       dev_driver_string(dev),
+						       (void *)isp_dev);
+				if (ret) {
+					dev_err(dev,
+						"req_irq fail, dev(%s) irq=%d\n",
+						dev->of_node->name,
+						isp_dev->irq);
+					return ret;
+				}
+				dev_info(dev, "Registered irq=%d, ISR: %s\n",
+					 isp_dev->irq, dev_driver_string(dev));
+			}
+		}
+		spin_lock_init(&isp_dev->spinlock_irq);
+	}
+
+	p1_dev->isp_clk.num_clks = ARRAY_SIZE(mtk_isp_clks);
+	p1_dev->isp_clk.clk_list =
+		devm_kcalloc(dev,
+			     p1_dev->isp_clk.num_clks,
+			     sizeof(*p1_dev->isp_clk.clk_list),
+			     GFP_KERNEL);
+	if (!p1_dev->isp_clk.clk_list)
+		return -ENOMEM;
+
+	for (i = 0; i < p1_dev->isp_clk.num_clks; ++i)
+		p1_dev->isp_clk.clk_list->id = mtk_isp_clks[i];
+
+	ret = devm_clk_bulk_get(dev,
+				p1_dev->isp_clk.num_clks,
+				p1_dev->isp_clk.clk_list);
+	if (ret) {
+		dev_err(dev, "cannot get isp cam clock:%d\n", ret);
+		return ret;
+	}
+
+	/* initialize the v4l2 common part */
+	ret = mtk_cam_dev_core_init(pdev, p1_dev->cam_dev);
+	if (ret)
+		return ret;
+
+	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
+	atomic_set(&p1_dev->isp_ctx.isp_user_cnt, 0);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+static int mtk_isp_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct isp_p1_device *p1_dev = dev_get_drvdata(dev);
+
+	pm_runtime_disable(dev);
+	mtk_cam_dev_core_release(pdev, p1_dev->cam_dev);
+
+	return 0;
+}
+
+static int mtk_isp_suspend(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct isp_device *isp_dev;
+	unsigned int reg_val;
+	int usercount, module;
+
+	module = p1_dev->isp_ctx.isp_hw_module;
+	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
+
+	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
+
+	/* If no user count, no further action */
+	if (!usercount)
+		return 0;
+
+	isp_dev = &p1_dev->isp_devs[module];
+	reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
+	if (reg_val & VFDATA_EN_BIT) {
+		dev_dbg(dev, "Cam:%d suspend, disable VF\n", module);
+		/* disable VF */
+		writel((reg_val & (~VFDATA_EN_BIT)),
+		       isp_dev->regs + REG_TG_VF_CON);
+		/*
+		 * After VF enable, The TG frame count will be reset to 0;
+		 */
+		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
+		writel((reg_val & (~CMOS_EN_BIT)),
+		       isp_dev->regs +  + REG_TG_SEN_MODE);
+	}
+
+	disable_sys_clock(p1_dev);
+
+	return 0;
+}
+
+static int mtk_isp_resume(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct isp_device *isp_dev;
+	unsigned int reg_val;
+	int module, usercount;
+
+	module = p1_dev->isp_ctx.isp_hw_module;
+	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
+
+	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
+
+	/* If no user count, no further action */
+	if (!usercount)
+		return 0;
+
+	enable_sys_clock(p1_dev);
+
+	/* V4L2 stream-on phase & restore HW stream-on status */
+	if (p1_dev->cam_dev->streaming) {
+		isp_dev = &p1_dev->isp_devs[module];
+		dev_dbg(dev, "Cam:%d resume,enable VF\n", module);
+		/* Enable CMOS */
+		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
+		writel((reg_val | CMOS_EN_BIT),
+		       isp_dev->regs + REG_TG_SEN_MODE);
+		/* Enable VF */
+		reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
+		writel((reg_val | VFDATA_EN_BIT),
+		       isp_dev->regs + REG_TG_VF_CON);
+	}
+	return 0;
+}
+
+static int isp_init_context(struct isp_p1_device *p1_dev)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = &p1_dev->pdev->dev;
+	unsigned int i;
+
+	dev_dbg(dev, "init irq work thread\n");
+	if (!isp_ctx->isp_deque_thread.thread) {
+		mutex_init(&isp_ctx->composer_tx_lock);
+		init_waitqueue_head(&isp_ctx->isp_deque_thread.wq);
+		isp_ctx->isp_deque_thread.thread =
+			kthread_run(isp_deque_work, (void *)p1_dev,
+				    "isp_deque_work");
+		if (IS_ERR(isp_ctx->isp_deque_thread.thread)) {
+			dev_err(dev, "unable to alloc kthread\n");
+			isp_ctx->isp_deque_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+	spin_lock_init(&isp_ctx->irq_dequeue_lock);
+
+	INIT_LIST_HEAD(&isp_ctx->p1_enqueue_list.queue);
+	atomic_set(&isp_ctx->p1_enqueue_list.queue_cnt, 0);
+
+	for (i = 0; i < ISP_DEV_NODE_NUM; i++)
+		spin_lock_init(&p1_dev->isp_devs[i].spinlock_irq);
+
+	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
+	spin_lock_init(&isp_ctx->composer_txlist.lock);
+
+	atomic_set(&isp_ctx->irq_data_end, 0);
+	atomic_set(&isp_ctx->irq_data_start, 0);
+	return 0;
+}
+
+static int isp_uninit_context(struct isp_p1_device *p1_dev)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct mtk_isp_queue_job *framejob, *tmp_framejob;
+
+	spin_lock_irq(&isp_ctx->p1_enqueue_list.lock);
+	list_for_each_entry_safe(framejob, tmp_framejob,
+				 &isp_ctx->p1_enqueue_list.queue, list_entry) {
+		list_del(&framejob->list_entry);
+		kfree(framejob);
+	}
+	spin_unlock_irq(&isp_ctx->p1_enqueue_list.lock);
+
+	atomic_set(&isp_ctx->isp_user_cnt, 0);
+
+	if (!IS_ERR(isp_ctx->isp_deque_thread.thread)) {
+		kthread_stop(isp_ctx->isp_deque_thread.thread);
+		wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
+		isp_ctx->isp_deque_thread.thread = NULL;
+	}
+
+	return 0;
+}
+
+/* Utility functions */
+static unsigned int get_sensor_pixel_id(unsigned int fmt)
+{
+	switch (fmt) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+		return raw_pxl_id_b;
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+		return raw_pxl_id_gb;
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+		return raw_pxl_id_gr;
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return raw_pxl_id_r;
+	default:
+		return raw_pxl_id_b;
+	}
+}
+
+static unsigned int get_sensor_fmt(unsigned int fmt)
+{
+	switch (fmt) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		return img_fmt_bayer8;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		return img_fmt_bayer10;
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		return img_fmt_bayer12;
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return img_fmt_bayer14;
+	default:
+		return img_fmt_unknown;
+	}
+}
+
+static unsigned int get_img_fmt(unsigned int fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_MTISP_B8:
+		return img_fmt_bayer8;
+	case V4L2_PIX_FMT_MTISP_F8:
+		return img_fmt_fg_bayer8;
+	case V4L2_PIX_FMT_MTISP_B10:
+		return img_fmt_bayer10;
+	case V4L2_PIX_FMT_MTISP_F10:
+		return img_fmt_fg_bayer10;
+	case V4L2_PIX_FMT_MTISP_B12:
+		return img_fmt_bayer12;
+	case V4L2_PIX_FMT_MTISP_F12:
+		return img_fmt_fg_bayer12;
+	case V4L2_PIX_FMT_MTISP_B14:
+		return img_fmt_bayer14;
+	case V4L2_PIX_FMT_MTISP_F14:
+		return img_fmt_fg_bayer14;
+	default:
+		return img_fmt_unknown;
+	}
+}
+
+static unsigned int get_pixel_byte(unsigned int fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_MTISP_B8:
+	case V4L2_PIX_FMT_MTISP_F8:
+		return 8;
+	case V4L2_PIX_FMT_MTISP_B10:
+	case V4L2_PIX_FMT_MTISP_F10:
+		return 10;
+	case V4L2_PIX_FMT_MTISP_B12:
+	case V4L2_PIX_FMT_MTISP_F12:
+		return 12;
+	case V4L2_PIX_FMT_MTISP_B14:
+	case V4L2_PIX_FMT_MTISP_F14:
+		return 14;
+	case V4L2_PIX_FMT_MTISP_U8:
+	case V4L2_PIX_FMT_MTISP_U10:
+	case V4L2_PIX_FMT_MTISP_U12:
+	case V4L2_PIX_FMT_MTISP_U14:
+		return 16;
+	default:
+		return 10;
+	}
+}
+
+static void composer_deinit_done_cb(void *data)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
+
+	disable_sys_clock(p1_dev);
+	/* Notify PM */
+	pm_runtime_put_sync(&p1_dev->pdev->dev);
+}
+
+/* ISP P1 interface functions */
+int mtk_isp_open(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	s32 usercount = atomic_inc_return(&isp_ctx->isp_user_cnt);
+	phandle rproc_phandle;
+	int ret;
+
+	dev_dbg(dev, "%s usercount=%d\n", __func__, usercount);
+
+	if (usercount == 1) {
+		p1_dev->scp_pdev = scp_get_pdev(p1_dev->pdev);
+		if (!p1_dev->scp_pdev) {
+			dev_err(dev, "Failed to get scp device\n");
+			return -EINVAL;
+		}
+		ret = of_property_read_u32(dev->of_node, "mediatek,scp",
+					   &rproc_phandle);
+		if (ret) {
+			dev_err(dev, "fail to get rproc_phandle:%d\n", ret);
+			return ret;
+		}
+
+		p1_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle);
+		dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n\n",
+			p1_dev->rproc_handle);
+		if (!p1_dev->rproc_handle) {
+			dev_err(dev, "fail to get rproc_handle\n");
+			return -EINVAL;
+		}
+
+		ret = rproc_boot(p1_dev->rproc_handle);
+		if (ret < 0) {
+			/*
+			 * Return 0 if downloading firmware successfully,
+			 * otherwise it is failed
+			 */
+			dev_err(dev, "rproc_boot failed:%d\n", ret);
+			return -EINVAL;
+		}
+
+		pm_runtime_get_sync(dev);
+
+		/* ISP HW INIT */
+		isp_ctx->isp_hw_module = ISP_CAM_B_IDX;
+		/* Use pure RAW as default HW path */
+		isp_ctx->isp_raw_path = ISP_PURE_RAW_PATH;
+
+		ret = isp_init_context(p1_dev);
+		if (ret)
+			return ret;
+		ret = isp_composer_init(isp_ctx);
+		if (ret)
+			return ret;
+		ret = isp_composer_hw_init(isp_ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int mtk_isp_release(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+
+	if (atomic_dec_and_test(&p1_dev->isp_ctx.isp_user_cnt)) {
+		isp_composer_hw_deinit(isp_ctx, composer_deinit_done_cb);
+		isp_uninit_context(p1_dev);
+	}
+
+	dev_dbg(dev, "%s usercount = %d\n", __func__,
+		atomic_read(&p1_dev->isp_ctx.isp_user_cnt));
+
+	return 0;
+}
+
+int mtk_isp_streamon(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct p1_config_param config_param;
+	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
+	struct v4l2_subdev_format sd_format;
+	unsigned int sd_width, sd_height;
+	unsigned int enable_dma, idx, i;
+	int ret;
+
+	p1_dev->isp_devs[isp_ctx->isp_hw_module].current_frame = 0;
+	p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count = 0;
+
+	isp_ctx->frame_seq_no = 1;
+	atomic_set(&isp_ctx->composed_frame_id, 0);
+
+	/* Get the enabled DMA ports */
+	enable_dma = 0;
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		if (cam_dev->mem2mem2_nodes[i].enabled)
+			enable_dma |=
+				cam_dev->mem2mem2_nodes[i].desc.dma_port;
+	}
+	dev_dbg(dev, "%s enable_dma:0x%x", __func__, enable_dma);
+
+	/* sensor config */
+	sd_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(cam_dev->sensor,
+			       pad, get_fmt, NULL, &sd_format);
+
+	if (ret) {
+		dev_dbg(dev, "sensor(%s) g_fmt on failed(%d)\n",
+			cam_dev->sensor->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev,
+		"sensor get_fmt ret=%d, w=%d, h=%d, code=0x%x, field=%d, color=%d\n",
+		ret, sd_format.format.width, sd_format.format.height,
+		sd_format.format.code, sd_format.format.field,
+		sd_format.format.colorspace);
+
+	config_param.cfg_in_param.continuous = 0x1;
+	config_param.cfg_in_param.subsample = 0x0;
+	/* fix to one pixel mode in default */
+	config_param.cfg_in_param.pixel_mode = one_pixel_mode;
+	/* support normal pattern in default */
+	config_param.cfg_in_param.data_pattern = 0x0;
+
+	config_param.cfg_in_param.crop.left = 0x0;
+	config_param.cfg_in_param.crop.top = 0x0;
+
+	config_param.cfg_in_param.raw_pixel_id =
+		get_sensor_pixel_id(sd_format.format.code);
+	config_param.cfg_in_param.img_fmt =
+		get_sensor_fmt(sd_format.format.code);
+	config_param.cfg_in_param.crop.width = sd_format.format.width;
+	config_param.cfg_in_param.crop.height = sd_format.format.height;
+	sd_width = sd_format.format.width;
+	sd_height = sd_format.format.height;
+
+	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	if ((enable_dma & R_IMGO) == R_IMGO) {
+		struct v4l2_format *imgo_fmt =
+			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+
+		config_param.cfg_main_param.pure_raw = isp_ctx->isp_raw_path;
+		config_param.cfg_main_param.pure_raw_pack = 1;
+		config_param.cfg_main_param.bypass = 0;
+
+		config_param.cfg_main_param.output.img_fmt =
+			get_img_fmt(imgo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_main_param.output.pixel_byte =
+			get_pixel_byte(imgo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_main_param.output.size.w =
+			imgo_fmt->fmt.pix_mp.width;
+		config_param.cfg_main_param.output.size.h =
+			imgo_fmt->fmt.pix_mp.height;
+
+		config_param.cfg_main_param.output.size.stride =
+			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+		config_param.cfg_main_param.output.size.xsize =
+			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+		config_param.cfg_main_param.output.crop.left = 0x0;
+		config_param.cfg_main_param.output.crop.top = 0x0;
+
+		config_param.cfg_main_param.output.crop.width = sd_width;
+		config_param.cfg_main_param.output.crop.height = sd_height;
+
+		WARN_ONCE(imgo_fmt->fmt.pix_mp.width > sd_width ||
+			  imgo_fmt->fmt.pix_mp.height > sd_height,
+			  "img out:%d:%d in:%d:%d",
+			  imgo_fmt->fmt.pix_mp.width,
+			  imgo_fmt->fmt.pix_mp.height,
+			  sd_width,
+			  sd_height);
+
+		dev_dbg(dev,
+			"imgo pixel_byte:%d img_fmt:0x%x raw:%d\n",
+			config_param.cfg_main_param.output.pixel_byte,
+			config_param.cfg_main_param.output.img_fmt,
+			config_param.cfg_main_param.pure_raw);
+		dev_dbg(dev,
+			"imgo param:size=(%0dx%0d),stride:%d,xsize:%d,crop=(%0dx%0d)\n",
+			config_param.cfg_main_param.output.size.w,
+			config_param.cfg_main_param.output.size.h,
+			config_param.cfg_main_param.output.size.stride,
+			config_param.cfg_main_param.output.size.xsize,
+			config_param.cfg_main_param.output.crop.width,
+			config_param.cfg_main_param.output.crop.height);
+	} else {
+		config_param.cfg_main_param.bypass = 1;
+	}
+
+	idx = MTK_CAM_P1_PACKED_BIN_OUT;
+	if ((enable_dma & R_RRZO) == R_RRZO) {
+		struct v4l2_format *rrzo_fmt =
+			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+
+		config_param.cfg_resize_param.bypass = 0;
+		config_param.cfg_resize_param.output.img_fmt =
+			get_img_fmt(rrzo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_resize_param.output.pixel_byte =
+			get_pixel_byte(rrzo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_resize_param.output.size.w =
+			rrzo_fmt->fmt.pix_mp.width;
+		config_param.cfg_resize_param.output.size.h =
+			rrzo_fmt->fmt.pix_mp.height;
+		config_param.cfg_resize_param.output.size.stride =
+			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+		config_param.cfg_resize_param.output.size.xsize =
+			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+		config_param.cfg_resize_param.output.crop.left = 0x0;
+		config_param.cfg_resize_param.output.crop.top = 0x0;
+		config_param.cfg_resize_param.output.crop.width = sd_width;
+		config_param.cfg_resize_param.output.crop.height = sd_height;
+
+		WARN_ONCE(rrzo_fmt->fmt.pix_mp.width > sd_width ||
+			  rrzo_fmt->fmt.pix_mp.height > sd_height,
+			  "rrz out:%d:%d in:%d:%d",
+			  rrzo_fmt->fmt.pix_mp.width,
+			  rrzo_fmt->fmt.pix_mp.height,
+			  sd_width,
+			  sd_height);
+
+		dev_dbg(dev, "rrzo pixel_byte:%d img_fmt:0x%x\n",
+			config_param.cfg_resize_param.output.pixel_byte,
+			config_param.cfg_resize_param.output.img_fmt);
+		dev_dbg(dev,
+			"rrzo param:size=(%0dx%0d),stride:%d,xsize:%d,crop=(%0dx%0d)\n",
+			config_param.cfg_resize_param.output.size.w,
+			config_param.cfg_resize_param.output.size.h,
+			config_param.cfg_resize_param.output.size.stride,
+			config_param.cfg_resize_param.output.size.xsize,
+			config_param.cfg_resize_param.output.crop.width,
+			config_param.cfg_resize_param.output.crop.height);
+	} else {
+		config_param.cfg_resize_param.bypass = 1;
+	}
+
+	/* Configure meta DMAs info. */
+	config_param.cfg_meta_param.enabled_meta_dmas = enable_dma;
+
+	isp_composer_hw_config(isp_ctx, &config_param);
+
+	/* Stream on */
+	isp_composer_stream(isp_ctx, 1);
+	dev_dbg(dev, "%s done\n", __func__);
+	return 0;
+}
+
+int mtk_isp_streamoff(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+
+	isp_composer_stream(isp_ctx, 0);
+	dev_dbg(dev, "%s done\n", __func__);
+
+	return 0;
+}
+
+int mtk_isp_enqueue(struct device *dev,
+		    struct mtk_cam_dev_start_param *frameparamsbase)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct p1_frame_param frameparams;
+	struct mtk_isp_queue_job *framejob;
+	struct mtk_cam_dev_buffer **bundle_buffers;
+	unsigned int i, idx;
+
+	framejob = kzalloc(sizeof(*framejob), GFP_ATOMIC);
+	memset(framejob, 0, sizeof(*framejob));
+	memset(&frameparams, 0, sizeof(frameparams));
+	INIT_LIST_HEAD(&framejob->list_buf);
+
+	bundle_buffers = &frameparamsbase->buffers[0];
+	frameparams.frame_seq_no = isp_ctx->frame_seq_no++;
+	frameparams.sof_idx =
+		p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count;
+	framejob->request_fd = frameparamsbase->request_fd;
+	framejob->frame_seq_no = frameparams.frame_seq_no;
+
+	idx = MTK_CAM_P1_META_IN_0;
+	if (bundle_buffers[idx]) {
+		frameparams.tuning_addr.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.tuning_addr.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Image output */
+	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	if (bundle_buffers[idx]) {
+		frameparams.img_dma_buffers[0].buffer.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.img_dma_buffers[0].buffer.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		dev_dbg(dev, "main stream address pa:0x%x iova:0x%x\n",
+			frameparams.img_dma_buffers[0].buffer.scp_addr,
+			frameparams.img_dma_buffers[0].buffer.iova);
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Resize output */
+	idx = MTK_CAM_P1_PACKED_BIN_OUT;
+	if (bundle_buffers[idx]) {
+		frameparams.img_dma_buffers[1].buffer.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.img_dma_buffers[1].buffer.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		dev_dbg(dev, "packed out address:0x%x iova:0x%x\n",
+			frameparams.img_dma_buffers[1].buffer.scp_addr,
+			frameparams.img_dma_buffers[1].buffer.iova);
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Meta output DMAs */
+	for (i = 0; i < MAX_META_DMA_NODES; i++) {
+		idx = MTK_CAM_P1_META_OUT_0 + i;
+		if (bundle_buffers[idx]) {
+			frameparams.meta_addrs[i].iova =
+			  bundle_buffers[idx]->daddr;
+			frameparams.meta_addrs[i].scp_addr =
+			  bundle_buffers[idx]->scp_addr;
+			list_add_tail(&bundle_buffers[idx]->list,
+				      &framejob->list_buf);
+		} else {
+			frameparams.meta_addrs[i].iova = 0;
+			frameparams.meta_addrs[i].scp_addr = 0;
+		}
+	}
+
+	spin_lock(&isp_ctx->p1_enqueue_list.lock);
+	list_add_tail(&framejob->list_entry, &isp_ctx->p1_enqueue_list.queue);
+	atomic_inc(&isp_ctx->p1_enqueue_list.queue_cnt);
+	spin_unlock(&isp_ctx->p1_enqueue_list.lock);
+
+	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_FRAME);
+	dev_dbg(dev, "request fd(0x%x) frame_seq_no(%d) is queued cnt:(%d)\n",
+		frameparamsbase->request_fd,
+		frameparams.frame_seq_no,
+		atomic_read(&isp_ctx->p1_enqueue_list.queue_cnt));
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_isp_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_suspend, mtk_isp_resume)
+	SET_RUNTIME_PM_OPS(mtk_isp_suspend, mtk_isp_resume, NULL)
+};
+
+static struct platform_driver mtk_isp_driver = {
+	.probe   = mtk_isp_probe,
+	.remove  = mtk_isp_remove,
+	.driver  = {
+		.name  = ISP_DEV_NAME,
+		.of_match_table = mtk_isp_of_ids,
+		.pm     = &mtk_isp_pm_ops,
+	}
+};
+
+module_platform_driver(mtk_isp_driver);
+
+MODULE_DESCRIPTION("Camera ISP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
new file mode 100644
index 0000000..6d13058
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
@@ -0,0 +1,288 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CAMERA_ISP_H
+#define __CAMERA_ISP_H
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/irqreturn.h>
+#include <linux/miscdevice.h>
+#include <linux/pm_qos.h>
+#include <linux/scatterlist.h>
+
+#include "mtk_cam-dev.h"
+#include "mtk_cam-ctx.h"
+#include "mtk_cam-scp.h"
+
+#define ISP_DEV_NAME		"mtk-cam"
+
+#define CAM_A_MAX_WIDTH		3328U
+#define CAM_A_MAX_HEIGHT	2496U
+#define CAM_B_MAX_WIDTH		5376U
+#define CAM_B_MAX_HEIGHT	4032U
+
+#define CAM_MIN_WIDTH		80U
+#define CAM_MIN_HEIGHT		60U
+
+#define IMG_MAX_WIDTH		CAM_B_MAX_WIDTH
+#define IMG_MAX_HEIGHT		CAM_B_MAX_HEIGHT
+#define IMG_MIN_WIDTH		CAM_MIN_WIDTH
+#define IMG_MIN_HEIGHT		CAM_MIN_HEIGHT
+
+#define RRZ_MAX_WIDTH		CAM_B_MAX_WIDTH
+#define RRZ_MAX_HEIGHT		CAM_B_MAX_HEIGHT
+#define RRZ_MIN_WIDTH		CAM_MIN_WIDTH
+#define RRZ_MIN_HEIGHT		CAM_MIN_HEIGHT
+
+#define R_IMGO		BIT(0)
+#define R_RRZO		BIT(1)
+#define R_AAO		BIT(3)
+#define R_AFO		BIT(4)
+#define R_LCSO		BIT(5)
+#define R_PDO		BIT(6)
+#define R_LMVO		BIT(7)
+#define R_FLKO		BIT(8)
+#define R_RSSO		BIT(9)
+#define R_PSO		BIT(10)
+
+#define ISP_COMPOSING_MAX_NUM		4
+#define ISP_FRAME_COMPOSING_MAX_NUM	3
+
+#define IRQ_DATA_BUF_SIZE		4
+#define COMPOSRE_EVENT_BUF_SIZE		4
+
+#define CQ_ADDRESS_OFFSET		0x640
+#define CQ_BUFFER_COUNT			3
+
+#define IRQ_STAT_STR "cam%c, SOF_%d irq(0x%x), " \
+			"dma(0x%x), frame_num(%d)/cq_num(%d)\n"
+
+/*
+ * In order with the sequence of device nodes defined in dtsi rule,
+ * one hardware module should be mapping to one node.
+ */
+enum isp_dev_node_enum {
+	ISP_CAMSYS_CONFIG_IDX = 0,
+	ISP_CAM_UNI_IDX,
+	ISP_CAM_A_IDX,
+	ISP_CAM_B_IDX,
+	ISP_DEV_NODE_NUM
+};
+
+/* Image RAW path for ISP P1 module. */
+enum isp_raw_path_enum {
+	ISP_PROCESS_RAW_PATH = 0,
+	ISP_PURE_RAW_PATH
+};
+
+enum {
+	img_fmt_unknown		= 0x0000,
+	img_fmt_raw_start	= 0x2200,
+	img_fmt_bayer8		= img_fmt_raw_start,
+	img_fmt_bayer10,
+	img_fmt_bayer12,
+	img_fmt_bayer14,
+	img_fmt_fg_bayer8,
+	img_fmt_fg_bayer10,
+	img_fmt_fg_bayer12,
+	img_fmt_fg_bayer14,
+};
+
+enum {
+	raw_pxl_id_b   = 0,
+	raw_pxl_id_gb,
+	raw_pxl_id_gr,
+	raw_pxl_id_r
+};
+
+enum {
+	default_pixel_mode = 0,
+	one_pixel_mode,
+	two_pixel_mode,
+	four_pixel_mode,
+	pixel_mode_num,
+};
+
+struct isp_queue {
+	struct list_head queue;
+	atomic_t queue_cnt;
+	spinlock_t lock; /* queue attributes protection */
+};
+
+struct isp_thread {
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+};
+
+enum mtk_isp_scp_ipi_type {
+	SCP_ISP_CMD = 0,
+	SCP_ISP_FRAME,
+};
+
+struct mtk_isp_queue_work {
+	union {
+		struct mtk_isp_scp_p1_cmd cmd;
+		struct p1_frame_param frameparams;
+	};
+	struct list_head list_entry;
+	enum mtk_isp_scp_ipi_type type;
+};
+
+struct mtk_isp_queue_job {
+	struct list_head list_entry;
+	struct list_head list_buf;
+	unsigned int request_fd;
+	unsigned int frame_seq_no;
+};
+
+struct isp_clk_struct {
+	int num_clks;
+	struct clk_bulk_data *clk_list;
+};
+
+struct isp_device {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	spinlock_t spinlock_irq; /* ISP reg setting integrity */
+	unsigned int current_frame;
+	u8 sof_count;
+	u8 isp_hw_module;
+};
+
+struct mtk_isp_p1_ctx {
+	atomic_t scp_state;
+	struct isp_queue composer_txlist;
+	struct isp_thread composer_tx_thread;
+	atomic_t cmd_queued;
+	struct mutex composer_tx_lock; /* isp composer work protection */
+
+	struct isp_thread composer_rx_thread;
+	struct mtk_isp_scp_p1_cmd composer_evts[COMPOSRE_EVENT_BUF_SIZE];
+	atomic_t composer_evts_start;
+	atomic_t composer_evts_end;
+	spinlock_t composer_evts_lock; /* SCP events protection */
+	/* increase after ipi */
+	atomic_t ipi_occupied;
+	/* increase after frame enqueue */
+	atomic_t composing_frame;
+	/* current composed frame id */
+	atomic_t composed_frame_id;
+
+	struct isp_queue p1_enqueue_list;
+
+	struct isp_thread isp_deque_thread;
+	struct mtk_cam_dev_stat_event_data irq_event_datas[IRQ_DATA_BUF_SIZE];
+	atomic_t irq_data_start;
+	atomic_t irq_data_end;
+	spinlock_t irq_dequeue_lock; /* ISP frame dequeuq protection */
+
+	dma_addr_t scp_mem_pa;
+	dma_addr_t scp_mem_iova;
+	struct sg_table sgtable;
+
+	/* increase after open, decrease when close */
+	atomic_t isp_user_cnt;
+	/* frame sequence number, increase per en-queue*/
+	int frame_seq_no;
+	unsigned int isp_hw_module;
+	unsigned int isp_raw_path;
+
+	void (*composer_deinit_donecb)(void *isp_ctx);
+
+	struct list_head list;
+};
+
+struct isp_p1_device {
+	struct platform_device *pdev;
+
+	/* for SCP driver  */
+	struct platform_device *scp_pdev;
+	struct rproc *rproc_handle;
+
+	struct mtk_isp_p1_ctx isp_ctx;
+	struct isp_clk_struct isp_clk;
+	struct mtk_cam_dev *cam_dev;
+	struct isp_device *isp_devs;
+};
+
+static inline struct isp_p1_device *
+p1_ctx_to_dev(const struct mtk_isp_p1_ctx *__p1_ctx)
+{
+	return container_of(__p1_ctx, struct isp_p1_device, isp_ctx);
+}
+
+static inline struct isp_p1_device *get_p1_device(struct device *dev)
+{
+	return ((struct isp_p1_device *)dev_get_drvdata(dev));
+}
+
+int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx);
+int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx);
+void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
+			    struct p1_config_param *config_param);
+void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on);
+void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
+			    void (*donecb)(void *data));
+void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
+			  void *data,
+			  enum mtk_isp_scp_ipi_type type);
+
+/**
+ * mtk_isp_open - open isp driver and initialize related resources.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_open(struct device *dev);
+
+/**
+ * mtk_isp_release - release isp driver and related resources.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_release(struct device *dev);
+
+/**
+ * mtk_isp_streamon - start to output image & meta data.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_streamon(struct device *dev);
+
+/**
+ * mtk_isp_streamoff -  stop to output image & meta data.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_streamoff(struct device *dev);
+
+/**
+ * mtk_isp_enqueue - enqueue a frame bundle to ISP driver.
+ *
+ * @dev:	isp device.
+ * @frameparamsbase: pointer to &struct mtk_cam_dev_start_param.
+ *
+ */
+int mtk_isp_enqueue(struct device *dev,
+		    struct mtk_cam_dev_start_param *frameparamsbase);
+
+#endif /*__CAMERA_ISP_H*/
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 11/12] media: platform: Add Mediatek ISP P1 SCP communication
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (9 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 10/12] media: platform: Add Mediatek ISP P1 device driver Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  2019-03-28  9:56   ` [RFC V1 12/12] media: platform: Add Mediatek ISP P1 shared memory driver Jungo Lin
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds communication with the co-processor on the SoC
through the SCP driver. It supports bi-directional command based
to exchange data and flow control.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
This patch dependents on "Add support for mt8183 SCP"[1].

[1] https://patchwork.kernel.org/cover/10872547/
---
---
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c      | 488 +++++++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h      | 215 +++++++++
 2 files changed, 703 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
new file mode 100644
index 0000000..be5fd36
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Seraph Huang <seraph.huang@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/kthread.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/remoteproc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include "mtk_cam.h"
+
+static int isp_composer_dma_sg_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	phys_addr_t scp_mem_va;
+	u32 size, size_align;
+	struct sg_table *sgt;
+	struct page **pages;
+	unsigned int n_pages, i;
+	int ret;
+
+	scp_mem_va = scp_get_reserve_mem_virt(SCP_ISP_MEM_ID);
+	isp_ctx->scp_mem_pa = scp_get_reserve_mem_phys(SCP_ISP_MEM_ID);
+	size = scp_get_reserve_mem_size(SCP_ISP_MEM_ID);
+
+	dev_dbg(dev, "isp scp mem: va:0x%llx, pa:0x%llx sz:0x%x\n",
+		scp_mem_va, isp_ctx->scp_mem_pa, size);
+
+	if (scp_mem_va != 0 && size > 0)
+		memset((void *)scp_mem_va, 0, size);
+
+	/* get iova address */
+	sgt = &isp_ctx->sgtable;
+	sg_alloc_table(sgt, 1, GFP_KERNEL);
+
+	size_align = round_up(size, PAGE_SIZE);
+	n_pages = size_align >> PAGE_SHIFT;
+
+	pages = kmalloc_array(n_pages, sizeof(struct page *),
+			      GFP_KERNEL);
+	if (!pages)
+		goto fail_pages_alloc;
+
+	for (i = 0; i < n_pages; i++)
+		pages[i] = phys_to_page(isp_ctx->scp_mem_pa + i * PAGE_SIZE);
+
+	ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+					0, size_align, GFP_KERNEL);
+	if (ret) {
+		dev_err(dev, "failed to allocate sg table:%d\n", ret);
+		goto fail_table_alloc;
+	}
+	sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents,
+				      DMA_BIDIRECTIONAL,
+				      DMA_ATTR_SKIP_CPU_SYNC);
+	if (!sgt->nents) {
+		dev_err(dev, "failed to dma sg map\n");
+		goto fail_map;
+	}
+
+	isp_ctx->scp_mem_iova = sg_dma_address(sgt->sgl);
+
+	return 0;
+
+fail_map:
+	sg_free_table(sgt);
+fail_table_alloc:
+	while (n_pages--)
+		__free_page(pages[n_pages]);
+fail_pages_alloc:
+	kfree(pages);
+	return -ENOMEM;
+}
+
+static void isp_composer_deinit(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct mtk_isp_queue_work *ipi_job, *tmp_ipi_job;
+
+	list_for_each_entry_safe(ipi_job, tmp_ipi_job,
+				 &isp_ctx->composer_txlist.queue,
+				 list_entry) {
+		list_del(&ipi_job->list_entry);
+		kfree(ipi_job);
+		atomic_dec(&isp_ctx->composer_txlist.queue_cnt);
+	}
+
+	atomic_set(&isp_ctx->ipi_occupied, 0);
+	atomic_set(&isp_ctx->composing_frame, 0);
+	atomic_set(&isp_ctx->scp_state, SCP_STATE_INVALID);
+	mutex_destroy(&isp_ctx->composer_tx_lock);
+
+	dma_unmap_sg_attrs(&p1_dev->pdev->dev, isp_ctx->sgtable.sgl,
+			   isp_ctx->sgtable.orig_nents,
+			   DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+	sg_free_table(&isp_ctx->sgtable);
+
+	if (!IS_ERR(isp_ctx->composer_tx_thread.thread)) {
+		kthread_stop(isp_ctx->composer_tx_thread.thread);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		isp_ctx->composer_tx_thread.thread = NULL;
+	}
+
+	isp_ctx->composer_deinit_donecb(isp_ctx);
+}
+
+/*
+ * Two kinds of flow control in isp_composer_tx_work.
+ *
+ * Case 1: IPI commands flow control. The maximum number of command queues is 3.
+ * There are two types of IPI commands (SCP_ISP_CMD/SCP_ISP_FRAME) in P1 driver.
+ * It is controlled by ipi_occupied.
+ * The priority of SCP_ISP_CMD is higher than SCP_ISP_FRAME.
+ *
+ * Case 2: Frame buffers flow control. The maximum number of frame buffers is 3.
+ * It is controlled by composing_frame.
+ * Frame buffer is sent by SCP_ISP_FRAME command.
+ */
+static int isp_composer_tx_work(void *data)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_isp_queue_work *isp_composer_work, *tmp_ipi_job;
+	struct isp_queue *composer_txlist = &isp_ctx->composer_txlist;
+	int ret;
+
+	while (1) {
+		ret = wait_event_interruptible
+			(isp_ctx->composer_tx_thread.wq,
+			 (atomic_read(&composer_txlist->queue_cnt) > 0 &&
+			 atomic_read(&isp_ctx->ipi_occupied) < 4 &&
+			 atomic_read(&isp_ctx->composing_frame) < 3) ||
+			 atomic_read(&isp_ctx->cmd_queued) > 0 ||
+			 kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock(&composer_txlist->lock);
+		if (atomic_read(&isp_ctx->cmd_queued) > 0) {
+			list_for_each_entry_safe(isp_composer_work, tmp_ipi_job,
+						 &composer_txlist->queue,
+						 list_entry) {
+				if (isp_composer_work->type == SCP_ISP_CMD) {
+					dev_dbg(dev, "Found a cmd\n");
+					break;
+				}
+			}
+		} else {
+			if (atomic_read(&isp_ctx->composing_frame) >=
+				ISP_FRAME_COMPOSING_MAX_NUM) {
+				spin_unlock(&composer_txlist->lock);
+				continue;
+			}
+			isp_composer_work =
+			    list_first_entry_or_null
+				(&composer_txlist->queue,
+				 struct mtk_isp_queue_work,
+				 list_entry);
+		}
+
+		list_del(&isp_composer_work->list_entry);
+		atomic_dec(&composer_txlist->queue_cnt);
+		spin_unlock(&composer_txlist->lock);
+
+		if (atomic_read(&isp_ctx->scp_state) == SCP_STATE_INVALID) {
+			dev_err(dev,
+				"ignore IPI type: %d, SCP state %d!\n",
+				isp_composer_work->type,
+				atomic_read(&isp_ctx->scp_state));
+			kfree(isp_composer_work);
+			continue;
+		}
+		if (isp_composer_work->type == SCP_ISP_CMD) {
+			mutex_lock(&isp_ctx->composer_tx_lock);
+			scp_ipi_send
+				(p1_dev->scp_pdev,
+				 SCP_IPI_ISP_CMD,
+				 &isp_composer_work->cmd,
+				 sizeof(isp_composer_work->cmd),
+				 0);
+			mutex_unlock(&isp_ctx->composer_tx_lock);
+			atomic_dec(&isp_ctx->cmd_queued);
+			atomic_inc(&isp_ctx->ipi_occupied);
+			dev_dbg(dev,
+				"%s cmd id %d sent, %d ipi buf occupied",
+				__func__,
+				isp_composer_work->cmd.cmd_id,
+				atomic_read(&isp_ctx->ipi_occupied));
+		} else if (isp_composer_work->type == SCP_ISP_FRAME) {
+			mutex_lock(&isp_ctx->composer_tx_lock);
+			scp_ipi_send
+				(p1_dev->scp_pdev,
+				 SCP_IPI_ISP_FRAME,
+				 &isp_composer_work->frameparams,
+				 sizeof(isp_composer_work->frameparams),
+				 0);
+			mutex_unlock(&isp_ctx->composer_tx_lock);
+			atomic_inc(&isp_ctx->ipi_occupied);
+			atomic_inc(&isp_ctx->composing_frame);
+			dev_dbg(dev,
+				"%s frame %d sent, %d ipi, %d CQ bufs occupied",
+				__func__,
+				isp_composer_work->frameparams.frame_seq_no,
+				atomic_read(&isp_ctx->ipi_occupied),
+				atomic_read(&isp_ctx->composing_frame));
+		} else {
+			dev_err(dev,
+				"ignore IPI type: %d!\n",
+				isp_composer_work->type);
+			kfree(isp_composer_work);
+			continue;
+		}
+		kfree(isp_composer_work);
+	}
+	return ret;
+}
+
+static int isp_composer_rx_work(void *data)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_isp_scp_p1_cmd ipi_msg;
+	int ret;
+	unsigned int ipi_queue_occupied, i;
+	unsigned long flags;
+	atomic_t *evts_end = &isp_ctx->composer_evts_end;
+	atomic_t *evts_start = &isp_ctx->composer_evts_start;
+	u8 ack_cmd_id;
+
+	while (1) {
+		ret = wait_event_interruptible(isp_ctx->composer_rx_thread.wq,
+					       (atomic_read(evts_end) !=
+					       atomic_read(evts_start)) ||
+					       kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock_irqsave(&isp_ctx->composer_evts_lock, flags);
+		i = atomic_read(evts_start);
+		memcpy(&ipi_msg,
+		       &isp_ctx->composer_evts[i],
+		       sizeof(struct mtk_isp_scp_p1_cmd));
+		atomic_set(evts_start, ++i & 0x3);
+		spin_unlock_irqrestore(&isp_ctx->composer_evts_lock, flags);
+
+		switch (ipi_msg.cmd_id) {
+		case ISP_CMD_SCP_STATE:
+			atomic_set(&isp_ctx->scp_state,
+				   ipi_msg.cmd_data[0]);
+			break;
+		case ISP_CMD_ACK:
+			ipi_queue_occupied =
+				atomic_dec_return(&isp_ctx->ipi_occupied);
+			if (ipi_queue_occupied >= ISP_COMPOSING_MAX_NUM)
+				wake_up_interruptible
+					(&isp_ctx->composer_tx_thread.wq);
+
+			ack_cmd_id = ipi_msg.ack_info.cmd_id;
+			if (ack_cmd_id == ISP_CMD_FRAME_ACK) {
+				dev_dbg(dev,
+					"%s frame %d ack\n",
+					__func__,
+					ipi_msg.ack_info.frame_seq_no);
+				atomic_set(&isp_ctx->composed_frame_id,
+					   ipi_msg.ack_info.frame_seq_no);
+			} else {
+				dev_dbg(dev, "%s cmd id: %d",
+					__func__,
+					ack_cmd_id);
+				if (ack_cmd_id == ISP_CMD_DEINIT) {
+					isp_composer_deinit(isp_ctx);
+					isp_ctx->composer_rx_thread.thread =
+						NULL;
+					return -1;
+				}
+			}
+			break;
+		default:
+			break;
+		};
+	}
+	return ret;
+}
+
+static void isp_composer_handler(void *data, unsigned int len, void *priv)
+{
+	struct mtk_isp_p1_ctx *isp_ctx;
+	struct mtk_isp_scp_p1_cmd *ipi_msg_ptr;
+	unsigned long flags;
+	unsigned int i;
+
+	ipi_msg_ptr = (struct mtk_isp_scp_p1_cmd *)data;
+	isp_ctx = (struct mtk_isp_p1_ctx *)priv;
+
+	spin_lock_irqsave(&isp_ctx->composer_evts_lock, flags);
+	i = atomic_read(&isp_ctx->composer_evts_end);
+	memcpy(&isp_ctx->composer_evts[i], data,
+	       sizeof(struct mtk_isp_scp_p1_cmd));
+	atomic_set(&isp_ctx->composer_evts_end, ++i & 0x3);
+	spin_unlock_irqrestore(&isp_ctx->composer_evts_lock, flags);
+
+	wake_up_interruptible(&isp_ctx->composer_rx_thread.wq);
+}
+
+int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	int ret;
+
+	atomic_set(&isp_ctx->scp_state, SCP_STATE_INVALID);
+
+	ret = scp_ipi_register(p1_dev->scp_pdev,
+			       SCP_IPI_ISP_CMD,
+			       isp_composer_handler,
+			       isp_ctx);
+	if (ret)
+		return ret;
+
+	if (!isp_ctx->composer_tx_thread.thread) {
+		mutex_init(&isp_ctx->composer_tx_lock);
+		init_waitqueue_head(&isp_ctx->composer_tx_thread.wq);
+		INIT_LIST_HEAD(&isp_ctx->composer_txlist.queue);
+		spin_lock_init(&isp_ctx->composer_txlist.lock);
+		isp_ctx->composer_tx_thread.thread =
+			kthread_run(isp_composer_tx_work, (void *)isp_ctx,
+				    "isp_composer_tx");
+		if (IS_ERR(isp_ctx->composer_tx_thread.thread)) {
+			dev_err(dev, "unable to start kthread\n");
+			isp_ctx->composer_tx_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+	atomic_set(&isp_ctx->composer_txlist.queue_cnt, 0);
+
+	if (!isp_ctx->composer_rx_thread.thread) {
+		init_waitqueue_head(&isp_ctx->composer_rx_thread.wq);
+		isp_ctx->composer_rx_thread.thread =
+			kthread_run(isp_composer_rx_work, (void *)isp_ctx,
+				    "isp_composer_rx");
+		if (IS_ERR(isp_ctx->composer_rx_thread.thread)) {
+			dev_err(dev, "unable to start kthread\n");
+			isp_ctx->composer_rx_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	atomic_set(&isp_ctx->composer_evts_start, 0);
+	atomic_set(&isp_ctx->composer_evts_end, 0);
+	spin_lock_init(&isp_ctx->composer_evts_lock);
+
+	atomic_set(&isp_ctx->ipi_occupied, 0);
+	atomic_set(&isp_ctx->composing_frame, 0);
+	atomic_set(&isp_ctx->scp_state, SCP_STATE_BOOTING);
+	atomic_set(&isp_ctx->cmd_queued, 0);
+
+	return 0;
+}
+
+void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
+			  void *data,
+			  enum mtk_isp_scp_ipi_type type)
+{
+	struct mtk_isp_queue_work *isp_composer_work;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+
+	isp_composer_work = kzalloc(sizeof(*isp_composer_work), GFP_KERNEL);
+	isp_composer_work->type = type;
+	switch (type) {
+	case SCP_ISP_CMD:
+		memcpy(&isp_composer_work->cmd, data,
+		       sizeof(isp_composer_work->cmd));
+
+		spin_lock(&isp_ctx->composer_txlist.lock);
+		list_add_tail(&isp_composer_work->list_entry,
+			      &isp_ctx->composer_txlist.queue);
+		atomic_inc(&isp_ctx->composer_txlist.queue_cnt);
+		spin_unlock(&isp_ctx->composer_txlist.lock);
+
+		dev_dbg(dev, "Enq ipi cmd id:%d\n",
+			isp_composer_work->cmd.cmd_id);
+		atomic_inc(&isp_ctx->cmd_queued);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		break;
+	case SCP_ISP_FRAME:
+		memcpy(&isp_composer_work->frameparams, data,
+		       sizeof(isp_composer_work->frameparams));
+
+		spin_lock(&isp_ctx->composer_txlist.lock);
+		list_add_tail(&isp_composer_work->list_entry,
+			      &isp_ctx->composer_txlist.queue);
+		atomic_inc(&isp_ctx->composer_txlist.queue_cnt);
+		spin_unlock(&isp_ctx->composer_txlist.lock);
+
+		dev_dbg(dev, "Enq ipi frame_num:%d\n",
+			isp_composer_work->frameparams.frame_seq_no);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		break;
+	default:
+		break;
+	}
+}
+
+int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct img_buffer frameparam;
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+	int ret;
+
+	ret = isp_composer_dma_sg_init(isp_ctx);
+	if (ret)
+		return ret;
+
+	frameparam.scp_addr = isp_ctx->scp_mem_pa;
+	frameparam.iova = isp_ctx->scp_mem_iova;
+
+	composer_tx_cmd.cmd_id = ISP_CMD_INIT;
+	composer_tx_cmd.frameparam.hw_module = isp_ctx->isp_hw_module;
+	memcpy(&composer_tx_cmd.frameparam.cq_addr, &frameparam,
+	       sizeof(struct img_buffer));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+
+	return 0;
+}
+
+void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
+			    struct p1_config_param *config_param)
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_CONFIG;
+	memcpy(&composer_tx_cmd.cmd_data[0], config_param,
+	       sizeof(struct p1_config_param));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
+
+void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on)
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_STREAM;
+	memcpy(&composer_tx_cmd.cmd_data[0], &on, sizeof(on));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
+
+void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
+			    void (*donecb)(void *data))
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_DEINIT;
+	isp_ctx->composer_deinit_donecb = donecb;
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
new file mode 100644
index 0000000..abc34e7
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Seraph Huang <seraph.huang@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_ISP_SCP_H
+#define _MTK_ISP_SCP_H
+
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/remoteproc.h>
+#include <linux/types.h>
+
+#define MAX_IMG_DMA_PORT	2
+#define MAX_META_DMA_PORT	8
+#define MAX_META_DMA_NODES	4
+
+/* describes the maximum size of a payload */
+#define MTK_IPI_CMD_SIZE	272
+
+/*
+ * struct img_size - image size information.
+ *
+ * @w: image width, the unit is pixel
+ * @h: image height, the unit is pixel
+ * @xsize: bytes per line based on width.
+ * @stride: bytes per line when changing line.
+ *          Normally, calculate new STRIDE based on
+ *          xsize + HW constrain(page or align).
+ *
+ */
+struct img_size {
+	__u32 w;
+	__u32 h;
+	__u32 xsize;
+	__u32 stride;
+} __packed;
+
+/*
+ * struct img_buffer - buffer address information.
+ *
+ * @iova: DMA address for external devices.
+ * @scp_addr: SCP address for external co-process unit.
+ *
+ */
+struct img_buffer {
+	__u32 iova;
+	__u32 scp_addr;
+} __packed;
+
+struct p1_img_crop {
+	__u32 left;
+	__u32 top;
+	__u32 width;
+	__u32 height;
+} __packed;
+
+struct p1_img_output {
+	struct img_buffer buffer;
+	struct img_size size;
+	struct p1_img_crop crop;
+	__u8 pixel_byte;
+	__u32 img_fmt;
+} __packed;
+
+/*
+ * struct cfg_in_param - image input parameters structure.
+ *                       Normally, it comes from sensor information.
+ *
+ * @continuous: indicate the sensor mode.
+ *              1: continuous
+ *              0: single
+ * @subsample: indicate to enables SOF subsample or not.
+ * @pixel_mode: describe 1/2/4 pixels per clock cycle.
+ * @data_pattern: describe input data pattern.
+ * @raw_pixel_id: bayer sequence.
+ * @tg_fps: the fps rate of TG (time generator).
+ * @img_fmt: the image format of input source.
+ * @p1_img_crop: the crop configuration of input source.
+ *
+ */
+struct cfg_in_param {
+	__u8 continuous;
+	__u8 subsample;
+	__u8 pixel_mode;
+	__u8 data_pattern;
+	__u8 raw_pixel_id;
+	__u16 tg_fps;
+	__u32 img_fmt;
+	struct p1_img_crop crop;
+} __packed;
+
+/*
+ * struct cfg_main_out_param - the image output parameters of main stream.
+ *
+ * @bypass: indicate this device is enabled or disabled or not .
+ * @pure_raw: indicate the image path control.
+ *            1: pure raw
+ *            0: processing raw
+ * @pure_raw_pack: indicate the image is packed or not.
+ *                 1: packed mode
+ *                 0: unpacked mode
+ * @p1_img_output: the output image information.
+ *
+ */
+struct cfg_main_out_param {
+	/* Bypass main out parameters */
+	__u8 bypass;
+	/* Control HW image raw path */
+	__u8 pure_raw;
+	/* Control HW image pack function */
+	__u8 pure_raw_pack;
+	struct p1_img_output output;
+} __packed;
+
+/*
+ * struct cfg_resize_out_param - the image output parameters of
+ *                               packed out stream.
+ *
+ * @bypass: indicate this device is enabled or disabled or not .
+ * @p1_img_output: the output image information.
+ *
+ */
+struct cfg_resize_out_param {
+	/* Bypass resize parameters */
+	__u8 bypass;
+	struct p1_img_output output;
+} __packed;
+
+/*
+ * struct cfg_meta_out_param - output meta information.
+ *
+ * @enabled_meta_dmas: indicate which meta DMAs are enabled.
+ *
+ */
+struct cfg_meta_out_param {
+	__u32 enabled_meta_dmas;
+} __packed;
+
+struct p1_config_param {
+	/* Sensor/TG info */
+	struct cfg_in_param cfg_in_param;
+	/* IMGO DMA */
+	struct cfg_main_out_param cfg_main_param;
+	/* RRZO DMA */
+	struct cfg_resize_out_param cfg_resize_param;
+	/* 3A DMAs and other. */
+	struct cfg_meta_out_param cfg_meta_param;
+} __packed;
+
+struct p1_frame_param {
+	/* frame sequence number */
+	__u32 frame_seq_no;
+	/* SOF index */
+	__u32 sof_idx;
+	/* The memory address of tuning buffer from user space */
+	struct img_buffer tuning_addr;
+	struct p1_img_output img_dma_buffers[MAX_IMG_DMA_PORT];
+	struct img_buffer meta_addrs[MAX_META_DMA_NODES];
+} __packed;
+
+struct isp_init_info {
+	__u8 hw_module;
+	struct img_buffer cq_addr;
+} __packed;
+
+struct isp_ack_info {
+	__u8 cmd_id;
+	__u32 frame_seq_no;
+} __packed;
+
+enum mtk_isp_scp_CMD {
+	ISP_CMD_INIT,
+	ISP_CMD_CONFIG,
+	ISP_CMD_STREAM,
+	ISP_CMD_DEINIT,
+	ISP_CMD_ACK,
+	ISP_CMD_SCP_STATE,
+	ISP_CMD_FRAME_ACK,
+	ISP_CMD_RESERVED,
+};
+
+struct mtk_isp_scp_p1_cmd {
+	__u8 cmd_id;
+	__u64 drv_data;
+	union {
+		struct isp_init_info frameparam;
+		struct p1_config_param config_param;
+		__u8 cmd_data[MTK_IPI_CMD_SIZE - sizeof(__u8) - sizeof(__u64)];
+		__u8 is_stream_on;
+		struct isp_ack_info ack_info;
+	};
+} __packed;
+
+enum mtk_isp_scp_p1_state {
+	SCP_STATE_INVALID = 0,
+	SCP_STATE_BOOTING,
+	SCP_STATE_RBREADY,
+};
+
+struct isp_scp_p1_param {
+	struct list_head list_entry;
+	struct mtk_isp_scp_p1_cmd cmd;
+};
+
+#endif /* _MTK_ISP_SCP_H */
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC V1 12/12] media: platform: Add Mediatek ISP P1 shared memory driver
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                     ` (10 preceding siblings ...)
  2019-03-28  9:56   ` [RFC V1 11/12] media: platform: Add Mediatek ISP P1 SCP communication Jungo Lin
@ 2019-03-28  9:56   ` Jungo Lin
  11 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-03-28  9:56 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

The purpose of this driver is to provide shared memory management
for exchanging tuning data between co-processor and the
Pass 1 unit of the camera ISP system.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c | 398 +++++++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-smem.h     |  25 ++
 2 files changed, 423 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c
new file mode 100644
index 0000000..9a92080
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-drv.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/dma-contiguous.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/iommu.h>
+#include <asm/cacheflush.h>
+
+#define MTK_CAM_SMEM_DEV_NAME "MTK-CAM-SMEM"
+
+struct mtk_cam_smem_drv {
+	struct platform_device *pdev;
+	struct sg_table sgt;
+	struct page **smem_pages;
+	int num_smem_pages;
+	dma_addr_t smem_base;
+	dma_addr_t smem_dma_base;
+	unsigned int smem_size;
+};
+
+/*
+ * MTK CAM SMEM DMA ops
+ */
+struct dma_coherent_mem {
+	void		*virt_base;
+	dma_addr_t	device_base;
+	unsigned long	pfn_base;
+	int		size;
+	int		flags;
+	unsigned long	*bitmap;
+	spinlock_t	spinlock; /* dma_coherent_mem attributes protection */
+	bool		use_dev_dma_pfn_offset;
+};
+
+static struct reserved_mem *isp_reserved_smem;
+
+dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *dev,
+					 dma_addr_t iova)
+{
+		struct iommu_domain *smem_dom;
+		dma_addr_t addr;
+		dma_addr_t limit;
+		struct mtk_cam_smem_drv *smem_dev =
+			dev_get_drvdata(dev);
+
+		smem_dom = iommu_get_domain_for_dev(dev);
+		if (!smem_dom) {
+			dev_warn(dev, "No iommu group domain");
+			return 0;
+		}
+
+		addr = iommu_iova_to_phys(smem_dom, iova);
+
+		limit = smem_dev->smem_base + smem_dev->smem_size;
+		if (addr < smem_dev->smem_base || addr >= limit) {
+			dev_err(dev,
+				"Unexpected scp_addr %pa (must >= %pa and <%pa)\n",
+				&addr, &smem_dev->smem_base, &limit);
+			return 0;
+		}
+		dev_dbg(dev, "Pa verifcation pass: %pa(>=%pa, <%pa)\n",
+			&addr, &smem_dev->smem_base, &limit);
+		return addr;
+}
+
+static int mtk_cam_smem_get_sgtable(struct device *dev,
+				    struct sg_table *sgt,
+	void *cpu_addr, dma_addr_t dma_addr,
+	size_t size, unsigned long attrs)
+{
+	struct mtk_cam_smem_drv *smem_dev = dev_get_drvdata(dev);
+	int n_pages_align = 0;
+	int size_align = 0;
+	int page_start = 0;
+	unsigned long long offset_p = 0;
+
+	dma_addr_t scp_addr = mtk_cam_smem_iova_to_scp_addr(dev, dma_addr);
+
+	offset_p = (unsigned long long)scp_addr -
+		(unsigned long long)smem_dev->smem_base;
+
+	size_align = round_up(size, PAGE_SIZE);
+	n_pages_align = size_align >> PAGE_SHIFT;
+	page_start = offset_p >> PAGE_SHIFT;
+	dev_dbg(dev,
+		"%s:page idx:%d,page pa:0x%llx,pa:0x%llx, aligned size:%d pages:%d\n",
+		__func__,
+		page_start,
+		(unsigned long long)page_to_phys(*(smem_dev->smem_pages
+			+ page_start)),
+		(unsigned long long)scp_addr,
+		size_align,
+		n_pages_align
+		);
+
+	return sg_alloc_table_from_pages(sgt,
+		smem_dev->smem_pages + page_start,
+		n_pages_align,
+		0, size_align, GFP_KERNEL);
+}
+
+static void *mtk_cam_smem_get_cpu_addr(struct mtk_cam_smem_drv *smem_dev,
+				       struct scatterlist *sg)
+{
+	struct device *dev = &smem_dev->pdev->dev;
+	struct dma_coherent_mem *dma_mem = dev->dma_mem;
+
+	dma_addr_t addr = (phys_addr_t)sg_phys(sg);
+
+	if (addr < smem_dev->smem_base ||
+	    addr > smem_dev->smem_base + smem_dev->smem_size) {
+		dev_err(dev, "Invalid scp_addr 0x%llx from sg\n", addr);
+		return NULL;
+	}
+
+	return dma_mem->virt_base + (addr - smem_dev->smem_base);
+}
+
+static void mtk_cam_smem_sync_sg_for_cpu(struct device *dev,
+					 struct scatterlist *sgl, int nelems,
+					 enum dma_data_direction dir)
+{
+	struct mtk_cam_smem_drv *smem_dev =
+		dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, sgl);
+	dev_dbg(dev,
+		"__dma_unmap_area:scp_addr(0x%llx),vaddr(0x%llx),size(%d),dir(%d)\n",
+		(unsigned long long)sg_phys(sgl),
+		(unsigned long long)cpu_addr,
+		sgl->length,
+		dir);
+	__dma_unmap_area(cpu_addr, sgl->length, dir);
+}
+
+static void mtk_cam_smem_sync_sg_for_device(struct device *dev,
+					    struct scatterlist *sgl,
+					    int nelems,
+					    enum dma_data_direction dir)
+{
+	struct mtk_cam_smem_drv *smem_dev =
+			dev_get_drvdata(dev);
+	void *cpu_addr;
+
+	cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, sgl);
+	flush_cache_vmap((unsigned long long)cpu_addr,
+			 (unsigned long long)cpu_addr + sgl->length);
+	__dma_flush_area(cpu_addr, sgl->length);
+	dev_dbg(dev,
+		"__dma_map_area:scp_addr(0x%llx),vaddr(0x%llx),size(%d),dir(%d)\n",
+		(unsigned long long)sg_phys(sgl),
+		(unsigned long long)cpu_addr,
+		sgl->length,
+		dir);
+	__dma_map_area(cpu_addr, sgl->length, dir);
+}
+
+static int mtk_cam_smem_setup_dma_ops(struct device *dev,
+				      const struct dma_map_ops *smem_ops)
+{
+	if (!dev->dma_ops)
+		return -EINVAL;
+
+	memcpy((void *)smem_ops, dev->dma_ops, sizeof(*smem_ops));
+	((struct dma_map_ops *)smem_ops)->get_sgtable =
+		mtk_cam_smem_get_sgtable;
+	((struct dma_map_ops *)smem_ops)->sync_sg_for_device =
+		mtk_cam_smem_sync_sg_for_device;
+	((struct dma_map_ops *)smem_ops)->sync_sg_for_cpu =
+		mtk_cam_smem_sync_sg_for_cpu;
+	dev->dma_ops = smem_ops;
+
+	return 0;
+}
+
+static const struct dma_map_ops smem_dma_ops = {
+	.get_sgtable = mtk_cam_smem_get_sgtable,
+};
+
+static int mtk_cam_smem_init(struct mtk_cam_smem_drv **mtk_cam_smem_drv_out,
+			     struct platform_device *pdev)
+{
+	struct mtk_cam_smem_drv *isp_sys;
+	struct device *dev = &pdev->dev;
+
+	isp_sys = devm_kzalloc(dev,
+			       sizeof(*isp_sys), GFP_KERNEL);
+	isp_sys->pdev = pdev;
+	*mtk_cam_smem_drv_out = isp_sys;
+
+	return 0;
+}
+
+static int mtk_cam_smem_drv_probe(struct platform_device *pdev)
+{
+	struct mtk_cam_smem_drv *smem_drv;
+	int r = 0;
+	struct device *dev = &pdev->dev;
+
+	dev_dbg(dev, "probe mtk_cam_smem_drv\n");
+
+	r = mtk_cam_smem_init(&smem_drv, pdev);
+
+	if (!smem_drv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, smem_drv);
+
+	if (isp_reserved_smem) {
+		dma_addr_t dma_addr;
+		dma_addr_t addr;
+		struct iommu_domain *smem_dom;
+		unsigned int i;
+		int size_align;
+		struct page **pages;
+		int n_pages;
+		struct sg_table *sgt = &smem_drv->sgt;
+
+		size_align = round_down(isp_reserved_smem->size,
+					PAGE_SIZE);
+		n_pages = size_align >> PAGE_SHIFT;
+
+		pages = kmalloc_array(n_pages, sizeof(struct page *),
+				      GFP_KERNEL);
+
+		if (!pages)
+			return -ENOMEM;
+
+		for (i = 0; i < n_pages; i++)
+			pages[i] = phys_to_page(isp_reserved_smem->base
+						+ i * PAGE_SIZE);
+
+		r = sg_alloc_table_from_pages(sgt, pages, n_pages, 0,
+					      size_align, GFP_KERNEL);
+
+		if (r) {
+			dev_err(dev, "failed to get alloca sg table\n");
+			return -ENOMEM;
+		}
+
+		dma_map_sg_attrs(dev, sgt->sgl, sgt->nents,
+				 DMA_BIDIRECTIONAL,
+				 DMA_ATTR_SKIP_CPU_SYNC);
+
+		dma_addr = sg_dma_address(sgt->sgl);
+		smem_dom = iommu_get_domain_for_dev(dev);
+		addr = iommu_iova_to_phys(smem_dom, dma_addr);
+
+		if (addr != isp_reserved_smem->base)
+			dev_err(dev,
+				"incorrect pa(0x%llx) should be 0x%llx\n",
+			(unsigned long long)addr,
+			(unsigned long long)isp_reserved_smem->base);
+
+		r = dma_declare_coherent_memory(dev,
+						isp_reserved_smem->base,
+			dma_addr, size_align, DMA_MEMORY_EXCLUSIVE);
+
+		dev_dbg(dev,
+			"Coherent mem base(0x%llx,%llx),size(%lx),ret(%d)\n",
+			isp_reserved_smem->base,
+			dma_addr, size_align, r);
+
+		smem_drv->smem_base = isp_reserved_smem->base;
+		smem_drv->smem_size = size_align;
+		smem_drv->smem_pages = pages;
+		smem_drv->num_smem_pages = n_pages;
+		smem_drv->smem_dma_base = dma_addr;
+
+		dev_dbg(dev, "smem_drv setting (0x%llx,%lx,0x%llx,%d)\n",
+			smem_drv->smem_base, smem_drv->smem_size,
+			(unsigned long long)smem_drv->smem_pages,
+			smem_drv->num_smem_pages);
+	}
+
+	r = mtk_cam_smem_setup_dma_ops(dev, &smem_dma_ops);
+
+	return r;
+}
+
+static int mtk_cam_smem_drv_remove(struct platform_device *pdev)
+{
+	struct mtk_cam_smem_drv *smem_drv =
+		dev_get_drvdata(&pdev->dev);
+
+	kfree(smem_drv->smem_pages);
+	return 0;
+}
+
+static int mtk_cam_smem_drv_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int mtk_cam_smem_drv_resume(struct device *dev)
+{
+	return 0;
+}
+
+static int mtk_cam_smem_drv_dummy_cb(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_cam_smem_drv_pm_ops = {
+	SET_RUNTIME_PM_OPS(&mtk_cam_smem_drv_dummy_cb,
+			   &mtk_cam_smem_drv_dummy_cb, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS
+		(&mtk_cam_smem_drv_suspend, &mtk_cam_smem_drv_resume)
+};
+
+static const struct of_device_id mtk_cam_smem_drv_of_match[] = {
+	{
+		.compatible = "mediatek,mt8183-cam_smem",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mtk_cam_smem_drv_of_match);
+
+static struct platform_driver mtk_cam_smem_driver = {
+	.probe = mtk_cam_smem_drv_probe,
+	.remove = mtk_cam_smem_drv_remove,
+	.driver = {
+		.name = MTK_CAM_SMEM_DEV_NAME,
+		.of_match_table =
+			of_match_ptr(mtk_cam_smem_drv_of_match),
+		.pm = &mtk_cam_smem_drv_pm_ops,
+	},
+};
+
+static int __init mtk_cam_smem_dma_setup(struct reserved_mem
+					 *rmem)
+{
+	unsigned long node = rmem->fdt_node;
+
+	if (of_get_flat_dt_prop(node, "reusable", NULL))
+		return -EINVAL;
+
+	if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
+		pr_err("Reserved memory: regions without no-map are not yet supported\n");
+		return -EINVAL;
+	}
+
+	isp_reserved_smem = rmem;
+
+	pr_err("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n",
+	       &rmem->base, (unsigned long)rmem->size / SZ_1M);
+	return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(mtk_cam_smem,
+		       "mediatek,reserve-memory-cam_smem",
+		       mtk_cam_smem_dma_setup);
+
+int __init mtk_cam_smem_drv_init(void)
+{
+	int ret;
+
+	pr_debug("platform_driver_register: mtk_cam_smem_driver\n");
+	ret = platform_driver_register(&mtk_cam_smem_driver);
+	if (ret)
+		pr_warn("isp smem drv init failed, driver didn't probe\n");
+
+	return ret;
+}
+subsys_initcall(mtk_cam_smem_drv_init);
+
+void __exit mtk_cam_smem_drv_ext(void)
+{
+	platform_driver_unregister(&mtk_cam_smem_driver);
+}
+module_exit(mtk_cam_smem_drv_ext);
+MODULE_AUTHOR("Frederic Chen <frederic.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek CAM shared memory driver");
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
new file mode 100644
index 0000000..719ec02
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_SMEM_H__
+#define __MTK_CAM_SMEM_H__
+
+#include <linux/dma-mapping.h>
+
+dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *smem_dev,
+					 dma_addr_t iova);
+
+#endif /*__MTK_CAM_SMEM_H__*/
+
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
@ 2019-04-02 10:04 ` Jungo Lin
  2019-04-02 11:33   ` Laurent Pinchart
  2019-04-03  1:44 ` [PATCH] media: media_device_enum_links32: clean a reserved field Jungo Lin
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-04-02 10:04 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Jungo Lin, Rynn.Wu, srv_heupstream, holmes.chiou,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang,
	yuzhao, christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

From: Jungo Lin <jungo.lin@mediatek.corp-partner.google.com>

In v4l2-compliance utility, test MEDIA_IOC_ENUM_ENTITIES
will check whether reserved field of media_links_enum filled
with zero. Reserved field is filled with zero in media_device_enum_links.

However, for 32 bit program, the reserved field is missing
copy from kernel space to user space in media_device_enum_links32
function.

This patch copies reserved field of media_links_enum from kernel space
to user space.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 drivers/media/media-device.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index b8ec886..f420829 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -502,6 +502,7 @@ static long media_device_enum_links32(struct media_device *mdev,
 {
 	struct media_links_enum links;
 	compat_uptr_t pads_ptr, links_ptr;
+	int ret;
 
 	memset(&links, 0, sizeof(links));
 
@@ -513,7 +514,15 @@ static long media_device_enum_links32(struct media_device *mdev,
 	links.pads = compat_ptr(pads_ptr);
 	links.links = compat_ptr(links_ptr);
 
-	return media_device_enum_links(mdev, &links);
+	ret = media_device_enum_links(mdev, &links);
+	if (ret)
+		return ret;
+
+	if (copy_to_user(ulinks->reserved, &links.reserved,
+			 sizeof(links.reserved)))
+		return -EFAULT;
+
+	return 0;
 }
 
 #define MEDIA_IOC_ENUM_LINKS32		_IOWR('|', 0x02, struct media_links_enum32)
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* Re: [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy
  2019-04-02 10:04 ` [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy Jungo Lin
@ 2019-04-02 11:33   ` Laurent Pinchart
  2019-04-03  0:30     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Laurent Pinchart @ 2019-04-02 11:33 UTC (permalink / raw)
  To: Jungo Lin
  Cc: ryan.yu, Jerry-ch.Chen, laurent.pinchart+renesas, Rynn.Wu,
	frankie.chiu, hans.verkuil, frederic.chen, seraph.huang,
	linux-media, holmes.chiou, sj.huang, Jungo Lin, linux-mediatek,
	matthias.bgg, mchehab, linux-arm-kernel, Sean.Cheng,
	srv_heupstream, tfiga, yuzhao, christie.yu, zwisler

Hi Jungo,

Thank you for the patch.

On Tue, Apr 02, 2019 at 06:04:04PM +0800, Jungo Lin wrote:
> From: Jungo Lin <jungo.lin@mediatek.corp-partner.google.com>
> 
> In v4l2-compliance utility, test MEDIA_IOC_ENUM_ENTITIES
> will check whether reserved field of media_links_enum filled
> with zero. Reserved field is filled with zero in media_device_enum_links.
> 
> However, for 32 bit program, the reserved field is missing
> copy from kernel space to user space in media_device_enum_links32
> function.
> 
> This patch copies reserved field of media_links_enum from kernel space
> to user space.
> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  drivers/media/media-device.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index b8ec886..f420829 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -502,6 +502,7 @@ static long media_device_enum_links32(struct media_device *mdev,
>  {
>  	struct media_links_enum links;
>  	compat_uptr_t pads_ptr, links_ptr;
> +	int ret;
>  
>  	memset(&links, 0, sizeof(links));
>  
> @@ -513,7 +514,15 @@ static long media_device_enum_links32(struct media_device *mdev,
>  	links.pads = compat_ptr(pads_ptr);
>  	links.links = compat_ptr(links_ptr);
>  
> -	return media_device_enum_links(mdev, &links);
> +	ret = media_device_enum_links(mdev, &links);
> +	if (ret)
> +		return ret;
> +
> +	if (copy_to_user(ulinks->reserved, &links.reserved,
> +			 sizeof(links.reserved)))
> +		return -EFAULT;

I think it would be better to zero the reserved field here instead of
copying it, as we know it has to be zero.

> +
> +	return 0;
>  }
>  
>  #define MEDIA_IOC_ENUM_LINKS32		_IOWR('|', 0x02, struct media_links_enum32)

-- 
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] 104+ messages in thread

* Re: [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy
  2019-04-02 11:33   ` Laurent Pinchart
@ 2019-04-03  0:30     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-04-03  0:30 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: ryan.yu, Jerry-ch.Chen, laurent.pinchart+renesas, Rynn.Wu,
	frankie.chiu, hans.verkuil, frederic.chen, seraph.huang,
	linux-media, holmes.chiou, sj.huang, Jungo Lin, linux-mediatek,
	matthias.bgg, mchehab, linux-arm-kernel, Sean.Cheng,
	srv_heupstream, tfiga, yuzhao, christie.yu, zwisler

Hi, Laurent,

On Tue, 2019-04-02 at 14:33 +0300, Laurent Pinchart wrote:
> Hi Jungo,
> 
> Thank you for the patch.
> 
> On Tue, Apr 02, 2019 at 06:04:04PM +0800, Jungo Lin wrote:
> > From: Jungo Lin <jungo.lin@mediatek.corp-partner.google.com>
> > 
> > In v4l2-compliance utility, test MEDIA_IOC_ENUM_ENTITIES
> > will check whether reserved field of media_links_enum filled
> > with zero. Reserved field is filled with zero in media_device_enum_links.
> > 
> > However, for 32 bit program, the reserved field is missing
> > copy from kernel space to user space in media_device_enum_links32
> > function.
> > 
> > This patch copies reserved field of media_links_enum from kernel space
> > to user space.
> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  drivers/media/media-device.c | 11 ++++++++++-
> >  1 file changed, 10 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> > index b8ec886..f420829 100644
> > --- a/drivers/media/media-device.c
> > +++ b/drivers/media/media-device.c
> > @@ -502,6 +502,7 @@ static long media_device_enum_links32(struct media_device *mdev,
> >  {
> >  	struct media_links_enum links;
> >  	compat_uptr_t pads_ptr, links_ptr;
> > +	int ret;
> >  
> >  	memset(&links, 0, sizeof(links));
> >  
> > @@ -513,7 +514,15 @@ static long media_device_enum_links32(struct media_device *mdev,
> >  	links.pads = compat_ptr(pads_ptr);
> >  	links.links = compat_ptr(links_ptr);
> >  
> > -	return media_device_enum_links(mdev, &links);
> > +	ret = media_device_enum_links(mdev, &links);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (copy_to_user(ulinks->reserved, &links.reserved,
> > +			 sizeof(links.reserved)))
> > +		return -EFAULT;
> 
> I think it would be better to zero the reserved field here instead of
> copying it, as we know it has to be zero.
> 

Got it.
We will revise the implementation and deliver the v2 patch.

Thanks,

Jungo 

> > +
> > +	return 0;
> >  }
> >  
> >  #define MEDIA_IOC_ENUM_LINKS32		_IOWR('|', 0x02, struct media_links_enum32)
> 



_______________________________________________
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] 104+ messages in thread

* [PATCH] media: media_device_enum_links32: clean a reserved field
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
  2019-04-02 10:04 ` [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy Jungo Lin
@ 2019-04-03  1:44 ` Jungo Lin
  2019-05-10  1:57 ` [RFC, V2, 00/11] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-04-03  1:44 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: Sean.Cheng, Rynn.Wu, srv_heupstream, holmes.chiou, ryan.yu,
	Jerry-ch.Chen, frankie.chiu, jungo.lin, sj.huang, yuzhao,
	christie.yu, seraph.huang, zwisler, linux-mediatek,
	frederic.chen, linux-arm-kernel, linux-media

In v4l2-compliance utility, test MEDIA_IOC_ENUM_ENTITIES
will check whether reserved field of media_links_enum filled
with zero.

However, for 32 bit program, the reserved field is missing
copy from kernel space to user space in media_device_enum_links32
function.

This patch adds the cleaning a reserved field logic in
media_device_enum_links32 function.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
Changes since v1
- Revised the reserved field handling logic

 drivers/media/media-device.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index b8ec886..6893843 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -502,6 +502,7 @@ static long media_device_enum_links32(struct media_device *mdev,
 {
 	struct media_links_enum links;
 	compat_uptr_t pads_ptr, links_ptr;
+	int ret;
 
 	memset(&links, 0, sizeof(links));
 
@@ -513,7 +514,13 @@ static long media_device_enum_links32(struct media_device *mdev,
 	links.pads = compat_ptr(pads_ptr);
 	links.links = compat_ptr(links_ptr);
 
-	return media_device_enum_links(mdev, &links);
+	ret = media_device_enum_links(mdev, &links);
+	if (ret)
+		return ret;
+
+	memset(ulinks->reserved, 0, sizeof(ulinks->reserved));
+
+	return 0;
 }
 
 #define MEDIA_IOC_ENUM_LINKS32		_IOWR('|', 0x02, struct media_links_enum32)
-- 
1.9.1


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 00/11] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
  2019-04-02 10:04 ` [PATCH v1] media: media_device_enum_links32: fix missing reserved field copy Jungo Lin
  2019-04-03  1:44 ` [PATCH] media: media_device_enum_links32: clean a reserved field Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-10  1:57 ` [RFC, V2, 01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, sj.huang, yuzhao,
	linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

Hello,

This RFC patch series adding the driver for Pass 1 (P1) unit in
Mediatek's camera ISP system on mt8183 SoC, which
will be used in camera features of CrOS. It's the first time Mediatek
develops ISP kernel drivers based on V4L2 and media controller
framework. I posted the main part of the ISP Pass 1 driver as RFC to
discuss first and would like some review comments on the overall
architecture of the driver.

Pass 1 unit processes image signal from sensor devices and accepts the
tuning parameters to adjust the image quality. It performs optical
black correction, defect pixel correction, W/IR imbalance correction
and lens shading correction for RAW processing.

The driver is implemented with V4L2 and media controller framework so
we have the following entities to describe the ISP pass 1 path. (The
current metadata interface used in meta input and partial meta nodes
is only a temporary solution to kick off the driver development and is
not ready to be reviewed yet):

1. meta input (output video device): connects to ISP P1 sub device. It
accepts the tuning buffer from user.

2. ISP P1 (sub device): connects to partial meta 0/1/2/3,
main stream and packed out video devices. When processing an image,
Pass 1 hardware supports multiple output images with different sizes
and formats so it needs two capture video devices ("main stream" and
"packed out") to return the image data to the user.

3. main stream (capture video device): return the processed image data
which is used in capture scenario.

4. packed out (capture video device): return the processed image data
which is used in preview scenario.

5. partial meta 0 (capture video device): return the AE/AWB statistics.

6. partial meta 1 (capture video device): return the AF statistics.

7. partial meta 2 (capture video device): return the local contrast
   enhanced statistics.

8. partial meta 3 (capture video device): return the local motion
   vector statistics.

The overall patches of the series is:

* Patch 1~4 are dt-bindings & dts information related to
  ISP P1 driver & shared memory.

* Patch 5 is Kconfig configuration for ISP P1 driver.

* Patch 6 extends the original V4L2 image & meta formats.

* Patch 7 add private v4l2 control ID for ISP P1 driver.

* Patch 8 provides V4L2 utility functions & default video devices
  configuration for ISP P1 driver.

* Patch 9 is the heart of ISP P1 driver. It handles the ISP
  HW configuration, provides interrupt handling and initializes
  the V4L2 device nodes and other functions.

* Patch 10 adds communication with the co-processor on the SoC through
  the SCP driver.
The SCP driver path is listed below:
<URL: https://patchwork.kernel.org/cover/10872547/>

* Patch 11 provides ISP P1 shard memory management between ISP P1 &
co-processor. It is controlled by child device of ISP P1 driver.

Here is ISP P1 media topology:
It is included the main/sub sensor & sen-inf sub-devices which are
implemented in below patch:
<URL: https://patchwork.kernel.org/cover/10852957/>

/usr/bin/media-ctl -d /dev/media2 -p

Media controller API version 4.19.36

Media device information
------------------------
driver          mtk-cam
model           MTK-ISP-P1-V4L2
serial
bus info        platform:1a000000.camisp
hw revision     0x0
driver version  4.19.36

Device topology
- entity 1: MTK-ISP-P1-V4L2 (12 pads, 8 links)
            type V4L2 subdev subtype Unknown flags 0
            device node name /dev/v4l-subdev4
	pad0: Sink
		<- "MTK-ISP-P1-V4L2 meta input":0 []
	pad1: Source
		-> "MTK-ISP-P1-V4L2 main stream":0 []
	pad2: Source
		-> "MTK-ISP-P1-V4L2 packed out":0 []
	pad3: Source
		-> "MTK-ISP-P1-V4L2 partial meta 0":0 []
	pad4: Source
		-> "MTK-ISP-P1-V4L2 partial meta 1":0 []
	pad5: Source
		-> "MTK-ISP-P1-V4L2 partial meta 2":0 [DYNAMIC]
	pad6: Source
		-> "MTK-ISP-P1-V4L2 partial meta 3":0 [DYNAMIC]
	pad7: Source
	pad8: Source
	pad9: Source
	pad10: Source
	pad11: Sink
		<- "1a040000.seninf.mipi-csi":4 [ENABLED]

- entity 14: MTK-ISP-P1-V4L2 meta input (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video15
	pad0: Sink
		-> "MTK-ISP-P1-V4L2":0 []

- entity 20: MTK-ISP-P1-V4L2 main stream (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video16
	pad0: Source
		<- "MTK-ISP-P1-V4L2":1 []

- entity 26: MTK-ISP-P1-V4L2 packed out (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video17
	pad0: Source
		<- "MTK-ISP-P1-V4L2":2 []

- entity 32: MTK-ISP-P1-V4L2 partial meta 0 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video18
	pad0: Source
		<- "MTK-ISP-P1-V4L2":3 []

- entity 38: MTK-ISP-P1-V4L2 partial meta 1 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video19
	pad0: Source
		<- "MTK-ISP-P1-V4L2":4 []

- entity 44: MTK-ISP-P1-V4L2 partial meta 2 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video20
	pad0: Source
		<- "MTK-ISP-P1-V4L2":5 [DYNAMIC]

- entity 50: MTK-ISP-P1-V4L2 partial meta 3 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video21
	pad0: Source
		<- "MTK-ISP-P1-V4L2":6 [DYNAMIC]

- entity 56: 1a040000.seninf.mipi-csi (12 pads, 3 links)
             type V4L2 subdev subtype Unknown flags 0
             device node name /dev/v4l-subdev5
	pad0: Sink
		<- "ov5695 2-0036":0 [ENABLED]
	pad1: Sink
		<- "ov2685 4-003c":0 []
	pad2: Sink
	pad3: Sink
	pad4: Source
		-> "MTK-ISP-P1-V4L2":11 [ENABLED]
	pad5: Source
	pad6: Source
	pad7: Source
	pad8: Source
	pad9: Source
	pad10: Source
	pad11: Source

- entity 69: ov5695 2-0036 (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev6
	pad0: Source
		[fmt:SBGGR10_1X10/2592x1944 field:none colorspace:srgb]
		-> "1a040000.seninf.mipi-csi":0 [ENABLED]

- entity 73: ov2685 4-003c (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev7
	pad0: Source
		[fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
		-> "1a040000.seninf.mipi-csi":1 []

===========
= history =
===========

version 2:
 - Add 3A enhancement feature which includes:
   Separates 3A pipeline out of frame basis to improve
   AE/AWB (exposure and white balance) performance.
   Add 2 SCP sub-commands for 3A meta buffers.
 - Add new child device to manage P1 shared memory between P1 HW unit
   and co-processor.
 - Remove mediatek,cam_smem.txt & cam_smem dts node in mt8183.dtsi.
 - Revised document wording for dt-bindings documents & dts information.
 - Remove mtk_cam-ctx.h & mtk_cam-dev-ctx-core.c and move these
   source codes to mtk_cam-dev.h & mtk_cam-dev.c.
 - mtk_cam-dev.h / mtk_cam-dev.c
   Revised mtk_cam_video_device & mtk_cam_dev to remove unused structure fields
   or add comments.
   Revised buffer size for LMVO & LCSO.
   Fix pixel format utility function.
   Add vb2_dma_contig_set_max_seg_size to configure DMA max segment size.
 - mtk_cam-v4l2-util.c
   Refactoring V4L2 async mechanism with seninf driver only
   Refactoring CIO (Connection IO) implementation with active sensor
   Revised stream on function for 3A enhancement feature
   Add new V4L2 en-queue/de-queue utility functions for 3A enhancement feature
 - mtk_cam-regs.h / mtk_cam.h / mtk_cam.c
   Add meta buffer index register definitions
   Add meta DMA configuration function.
   Separate with frame-base and non-frame-base en-queue/de-queue functions
   Add isp_setup_scp_rproc function to get RPC handle
   Add mtk_cam_reserved_memory_init for shared memory management
 - mtk_cam-scp.h / mtk_cam-scp.c
   Add new meta strictures for 3A enhancement feature
   Add new IPI command utility function for 3A enhancement feature
   Enhance isp_composer_dma_sg_init function flow
   Shorten overall IPI command structure size
   Remove scp_state state checking
   Improve code readability
 - mtk_cam-smem.h / mtk_cam-smem-dev.c
   Add mtk_cam_alloc_smem_dev to allocate one new child device of ISP driver.
   Handling P1 driver 's reserved memory & allocate DMA buffers based on this
   memory region.

TODOs:
 - 3A enhancement feature bug fixing

version 1:
 - Revised driver soruces based on Tomasz's comments including
   part1/2/3/4 in RFC V0 patch.
 - Remove DMA cache mechanism.
   Support two new video devices (LCSO/LMVO) for advance camera
   features.
 - Fixed v4l2-compliance test failure items.
 - Add private controls for Mediatek camera middleware.
 - Replace VPU driver's APIs with new SCP driver interface for
   co-processor communication.
 - Refactoring mtk_cam_scp.c to use ring-buffers mechanism for IPI
   commands RX handling.
 - Fix internal bugs.

TODOs:
 - Remove mtk_cam_smem_drv.c & mtk_cam_smem.h and implement DMA pool
   for shared memory management.
 - Revised file names.
 - Support non frame-sync AFO/AAO DMA buffers

version 0:
- Initial submission

==================
 Dependent patch
==================

Camera ISP P1 driver depends on seninf driver, SCP driver & IOMMU driver.
The patches are as following:

[1]. media: support Mediatek sensor interface driver
https://patchwork.kernel.org/cover/10852957/

[2]. Add support for mt8183 SCP
https://patchwork.kernel.org/cover/10872547/

[3]. MT8183 IOMMU SUPPORT
https://patchwork.kernel.org/cover/10719217/

==================
 Compliance test
==================

The v4l2-compliance is built with the below lastest patch.
https://git.linuxtv.org/v4l-utils.git/commit/utils/v4l2-compliance?id=2aff3ef768c42cfdbb31d143ee2286a6b46e9db0

Note 1.
Revised testRequests function to bypass V4L2_CTRL_FLAG_READ_ONLY to
avoid VIDIOC_S_EXT_CTRLS failure due to EACASS with read only flag.

[Code]
	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ||
		qctrl.flags & V4L2_CTRL_FLAG_WRITE_ONLY)
		continue;

Note 2.
Before passing streaming on testing, need to enable media links betwen
entity 1: MTK-ISP-P1-V4L2, entity 20: MTK-ISP-P1-V4L2 main stream
and entity 26: MTK-ISP-P1-V4L2 packed out.

/usr/bin/media-ctl -i
Enter a link to modify or enter to stop
1:1->20:0[1]
1:1->20:0[1]
Enter a link to modify or enter to stop
1:2->26:0[1]
1:2->26:0[1]
Enter a link to modify or enter to stop

v4l2-compliance test output:
/usr/bin/v4l2-compliance -m /dev/media2

v4l2-compliance SHA: not available, 32 bits

Compliance test for mtk-cam device /dev/media2:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36

Required ioctls:
	test MEDIA_IOC_DEVICE_INFO: OK

Allow for multiple opens:
	test second /dev/media2 open: OK
	test MEDIA_IOC_DEVICE_INFO: OK
	test for unlimited opens: OK

Media Controller ioctls:
	test MEDIA_IOC_G_TOPOLOGY: OK
	Entities: 11 Interfaces: 11 Pads: 33 Links: 21
	test MEDIA_IOC_ENUM_ENTITIES/LINKS: OK
	test MEDIA_IOC_SETUP_LINK: OK

Total for mtk-cam device /dev/media2: 7, Succeeded: 7, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video15:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	Capabilities     : 0x8c200000
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x0c200000
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000010
	Type             : V4L Video
Entity Info:
	ID               : 0x0000000e (14)
	Name             : MTK-ISP-P1-V4L2 meta input
	Function         : V4L2 I/O
	Pad 0x0100000f   : 0: Sink
	  Link 0x02000012: to remote pad 0x1000002 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video15 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video15: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video16:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	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      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000016
	Type             : V4L Video
Entity Info:
	ID               : 0x00000014 (20)
	Name             : MTK-ISP-P1-V4L2 main stream
	Function         : V4L2 I/O
	Pad 0x01000015   : 0: Source
	  Link 0x02000018: from remote pad 0x1000003 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video16 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: 1 Private Controls: 2

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)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	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
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video16: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video17:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	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      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x0300001c
	Type             : V4L Video
Entity Info:
	ID               : 0x0000001a (26)
	Name             : MTK-ISP-P1-V4L2 packed out
	Function         : V4L2 I/O
	Pad 0x0100001b   : 0: Source
	  Link 0x0200001e: from remote pad 0x1000004 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video17 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: 1 Private Controls: 2

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)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	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
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video17: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video18:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000022
	Type             : V4L Video
Entity Info:
	ID               : 0x00000020 (32)
	Name             : MTK-ISP-P1-V4L2 partial meta 0
	Function         : V4L2 I/O
	Pad 0x01000021   : 0: Source
	  Link 0x02000024: from remote pad 0x1000005 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video18 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video18: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video19:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000028
	Type             : V4L Video
Entity Info:
	ID               : 0x00000026 (38)
	Name             : MTK-ISP-P1-V4L2 partial meta 1
	Function         : V4L2 I/O
	Pad 0x01000027   : 0: Source
	  Link 0x0200002a: from remote pad 0x1000006 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video19 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video19: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video20:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x0300002e
	Type             : V4L Video
Entity Info:
	ID               : 0x0000002c (44)
	Name             : MTK-ISP-P1-V4L2 partial meta 2
	Function         : V4L2 I/O
	Pad 0x0100002d   : 0: Source
	  Link 0x02000030: from remote pad 0x1000007 of entity 'MTK-ISP-P1-V4L2': Data, Dynamic

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video20 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video20: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video21:

Driver Info:
	Driver name      : MTK-ISP-P1-V4L2
	Card type        : MTK-ISP-P1-V4L2
	Bus info         : platform:1a000000.camisp
	Driver version   : 4.19.36
	Capabilities     : 0x84a00000
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04a00000
		Metadata Capture
		Streaming
		Extended Pix Format
Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000034
	Type             : V4L Video
Entity Info:
	ID               : 0x00000032 (50)
	Name             : MTK-ISP-P1-V4L2 partial meta 3
	Function         : V4L2 I/O
	Pad 0x01000033   : 0: Source
	  Link 0x02000036: from remote pad 0x1000008 of entity 'MTK-ISP-P1-V4L2': Data, Dynamic

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second /dev/video21 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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK
	test VIDIOC_TRY_FMT: OK
	test VIDIOC_S_FMT: OK
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
	test VIDIOC_EXPBUF: OK
	test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video21: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev4:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x0300004f
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000001 (1)
	Name             : MTK-ISP-P1-V4L2
	Function         : Video Statistics
	Pad 0x01000002   : 0: Sink
	  Link 0x02000012: from remote pad 0x100000f of entity 'MTK-ISP-P1-V4L2 meta input': Data
	Pad 0x01000003   : 1: Source
	  Link 0x02000018: to remote pad 0x1000015 of entity 'MTK-ISP-P1-V4L2 main stream': Data, Enabled
	Pad 0x01000004   : 2: Source
	  Link 0x0200001e: to remote pad 0x100001b of entity 'MTK-ISP-P1-V4L2 packed out': Data, Enabled
	Pad 0x01000005   : 3: Source
	  Link 0x02000024: to remote pad 0x1000021 of entity 'MTK-ISP-P1-V4L2 partial meta 0': Data
	Pad 0x01000006   : 4: Source
	  Link 0x0200002a: to remote pad 0x1000027 of entity 'MTK-ISP-P1-V4L2 partial meta 1': Data
	Pad 0x01000007   : 5: Source
	  Link 0x02000030: to remote pad 0x100002d of entity 'MTK-ISP-P1-V4L2 partial meta 2': Data, Dynamic
	Pad 0x01000008   : 6: Source
	  Link 0x02000036: to remote pad 0x1000033 of entity 'MTK-ISP-P1-V4L2 partial meta 3': Data, Dynamic
	Pad 0x01000009   : 7: Source
	Pad 0x0100000a   : 8: Source
	Pad 0x0100000b   : 9: Source
	Pad 0x0100000c   : 10: Source
	Pad 0x0100000d   : 11: Sink
	  Link 0x0200004d: from remote pad 0x100003d of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev4 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 1):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 2):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 3):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 11):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
	test VIDIOC_QUERYCTRL: OK (Not Supported)
	test VIDIOC_G/S_CTRL: OK (Not Supported)
	test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
	test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
	test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
	Standard Controls: 0 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev4: 125, Succeeded: 125, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev5:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000051
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000038 (56)
	Name             : 1a040000.seninf.mipi-csi
	Function         : Video Interface Bridge
	Pad 0x01000039   : 0: Sink
	  Link 0x02000047: from remote pad 0x1000046 of entity 'ov5695 2-0036': Data
	Pad 0x0100003a   : 1: Sink
	  Link 0x0200004b: from remote pad 0x100004a of entity 'ov2685 4-003c': Data
	Pad 0x0100003b   : 2: Sink
	Pad 0x0100003c   : 3: Sink
	Pad 0x0100003d   : 4: Source
	  Link 0x0200004d: to remote pad 0x100000d of entity 'MTK-ISP-P1-V4L2': Data
	Pad 0x0100003e   : 5: Source
	Pad 0x0100003f   : 6: Source
	Pad 0x01000040   : 7: Source
	Pad 0x01000041   : 8: Source
	Pad 0x01000042   : 9: Source
	Pad 0x01000043   : 10: Source
	Pad 0x01000044   : 11: Source

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev5 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 1):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 2):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 3):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 11):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 2 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev5: 125, Succeeded: 125, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev6:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000053
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000045 (69)
	Name             : ov5695 2-0036
	Function         : Camera Sensor
	Pad 0x01000046   : 0: Source
	  Link 0x02000047: to remote pad 0x1000039 of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev6 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 11 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev6: 48, Succeeded: 48, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev7:

Media Driver Info:
	Driver name      : mtk-cam
	Model            : MTK-ISP-P1-V4L2
	Serial           : 
	Bus info         : platform:1a000000.camisp
	Media version    : 4.19.36
	Hardware revision: 0x00000000 (0)
	Driver version   : 4.19.36
Interface Info:
	ID               : 0x03000055
	Type             : V4L Sub-Device
Entity Info:
	ID               : 0x00000049 (73)
	Name             : ov2685 4-003c
	Function         : Camera Sensor
	Pad 0x0100004a   : 0: Source
	  Link 0x0200004b: to remote pad 0x100003a of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
	test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
	test second /dev/v4l-subdev7 open: OK
	test for unlimited opens: OK

Debug ioctls:
	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 (Not Supported)
	test VIDIOC_G/S_AUDIO: OK (Not Supported)
	Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
	test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Try VIDIOC_SUBDEV_G/S_FMT: OK
	test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
	test Active VIDIOC_SUBDEV_G/S_FMT: OK
	test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
	test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
	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: 10 Private Controls: 0

Format ioctls:
	test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
	test VIDIOC_G/S_PARM: OK (Not Supported)
	test VIDIOC_G_FBUF: OK (Not Supported)
	test VIDIOC_G_FMT: OK (Not Supported)
	test VIDIOC_TRY_FMT: OK (Not Supported)
	test VIDIOC_S_FMT: OK (Not Supported)
	test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
	test Cropping: OK (Not Supported)
	test Composing: OK (Not Supported)
	test Scaling: OK (Not Supported)

Codec ioctls:
	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:
	test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
	test VIDIOC_EXPBUF: OK (Not Supported)
	test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev7: 48, Succeeded: 48, Failed: 0, Warnings: 0

Grand Total for mtk-cam device /dev/media2: 668, Succeeded: 668, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------

Jungo Lin (11):
  dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
  dts: arm64: mt8183: Add ISP Pass 1 shared memory node
  dt-bindings: mt8183: Added camera ISP Pass 1
  dts: arm64: mt8183: Add ISP Pass 1 nodes
  media: platform: Add Mediatek ISP Pass 1 driver Kconfig
  media: platform: Add Mediatek ISP P1 image & meta formats
  media: platform: Add Mediatek ISP P1 private control
  media: platform: Add Mediatek ISP P1 V4L2 functions
  media: platform: Add Mediatek ISP P1 device driver
  media: platform: Add Mediatek ISP P1 SCP communication
  media: platform: Add Mediatek ISP P1 shared memory device

 .../bindings/media/mediatek,camisp.txt        |   92 ++
 .../mediatek,reserve-memory-cam-smem.txt      |   42 +
 arch/arm64/boot/dts/mediatek/mt8183.dtsi      |   45 +
 drivers/media/platform/Kconfig                |    2 +
 drivers/media/platform/mtk-isp/Kconfig        |   21 +
 .../platform/mtk-isp/isp_50/cam/Makefile      |   19 +
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c         |  133 ++
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h         |   32 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.c |  758 +++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.h |  250 ++++
 .../mtk-isp/isp_50/cam/mtk_cam-regs.h         |  149 ++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c |  481 +++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h |  207 +++
 .../mtk-isp/isp_50/cam/mtk_cam-smem-dev.c     |  297 ++++
 .../mtk-isp/isp_50/cam/mtk_cam-smem.h         |   28 +
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c    | 1086 +++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h    |   43 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam.c     | 1206 +++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam.h     |  300 ++++
 include/uapi/linux/v4l2-controls.h            |    4 +
 include/uapi/linux/videodev2.h                |   20 +
 21 files changed, 5215 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt
 create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt
 create mode 100644 drivers/media/platform/mtk-isp/Kconfig
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h

-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (2 preceding siblings ...)
  2019-05-10  1:57 ` [RFC, V2, 00/11] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-14 19:50   ` [RFC,V2,01/11] " Rob Herring
  2019-05-10  1:57 ` [RFC,V2,02/11] dts: arm64: mt8183: Add ISP Pass 1 shared memory node Jungo Lin
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds the binding for describing the reserved
shared memory used to exchange ISP configuration and tuning
data between the co-processor and Pass 1 (P1) unit of the
camera ISP system on Mediatek SoCs.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../mediatek,reserve-memory-cam-smem.txt      | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt

diff --git a/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt b/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt
new file mode 100644
index 000000000000..65a967cff91e
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt
@@ -0,0 +1,42 @@
+Mediatek ISP Pass 1 Shared Memory binding
+
+This binding describes the shared memory, which serves the purpose of
+describing the shared memory region used to exchange data between Pass 1
+unit of Image Signal Processor (ISP) and the co-processor in Mediatek
+SoCs.
+
+The co-processor doesn't have the iommu so we need to use the physical
+address to access the shared buffer in the firmware.
+
+The Pass 1 unit of ISP can access memory through the iommu so it
+uses the dma address to access the memory region.
+(See iommu/mediatek,iommu.txt for the detailed description of Mediatek IOMMU)
+
+For additional details about reserved memory regions see reserved-memory.txt
+
+Required properties:
+
+- compatible: must be "mediatek,reserve-memory-cam-smem"
+
+- size: required for dynamic allocation. The unit is bytes.
+
+- alloc-range: required for dynamic allocation. The range must
+  between 0x40000000 and 0x100000000 due to the co-processer's
+  addressing limitation.
+
+Example:
+
+The following example shows the ISP Pass 1 shared memory setup for MT8183.
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		cam_mem_reserved: cam_mem_region {
+			compatible = "mediatek,reserve-memory-cam-smem";
+			size = <0 0x1400000>;
+			no-map;
+			alignment = <0 0x1000>;
+			alloc-ranges = <0 0x40000000 0 0x10000000>;
+		};
+	};
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,02/11] dts: arm64: mt8183: Add ISP Pass 1 shared memory node
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (3 preceding siblings ...)
  2019-05-10  1:57 ` [RFC, V2, 01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-10  1:57 ` [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds a shared memory region used on mt8183 for
exchanging tuning data between co-processor and the
Pass 1 unit of the camera ISP system.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index 75c4881bbe5e..d5d83a05f8a1 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -133,6 +133,19 @@
 		clock-output-names = "clk26m";
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		cam_mem_reserved: cam_mem_region {
+			compatible = "mediatek,reserve-memory-cam-smem";
+			no-map;
+			size = <0 0x01400000>; /* 20 MB share mem size */
+			alignment = <0 0x1000>;
+			alloc-ranges = <0 0x40000000 0 0x10000000>;
+		};
+	};
+
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupt-parent = <&gic>;
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (4 preceding siblings ...)
  2019-05-10  1:57 ` [RFC,V2,02/11] dts: arm64: mt8183: Add ISP Pass 1 shared memory node Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-14 19:54   ` Rob Herring
  2019-05-10  1:57 ` [RFC,V2,04/11] dts: arm64: mt8183: Add ISP Pass 1 nodes Jungo Lin
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds DT binding document for the Pass 1 (P1) unit in
Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data
out from the sensor interface, applies ISP image effects from tuning
data and outputs the image data or statistics data to DRAM.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../bindings/media/mediatek,camisp.txt        | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt

diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
new file mode 100644
index 000000000000..759e55a5dfac
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
@@ -0,0 +1,92 @@
+* Mediatek Image Signal Processor Pass 1 (ISP P1)
+
+The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out
+from the sensor interface, applies ISP effects from tuning data and outputs
+the image data and statistics data to DRAM. Furthermore, Pass 1 unit has
+the ability to output two different resolutions frames at the same time to
+increase the performance of the camera application.
+
+Required properties:
+- compatible: Must be "mediatek,mt8183-camisp" for MT8183.
+- reg: Must contain an entry for each entry in reg-names.
+- interrupts: interrupt number to the cpu.
+- iommus: shall point to the respective IOMMU block with master port
+  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- power-domains : a phandle to the power domain of this local arbiter.
+- clocks: device clocks, see
+  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- clock-names: must be "CAMSYS_CAM_CGPDN" and "CAMSYS_CAMTG_CGPDN".
+- mediatek,larb: must contain the local arbiters in the current SOCs, see
+  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+  for details.
+- mediatek,scp : the node of system control processor (SCP), see
+  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
+- memory-region : the reserved shared memory region between Pass 1 unit and
+  system control processor.
+
+Example:
+SoC specific DT entry:
+
+	camisp: camisp@1a000000 {
+		compatible = "mediatek,mt8183-camisp", "syscon";
+		reg = <0 0x1a000000 0 0x1000>,
+		      <0 0x1a003000 0 0x1000>,
+		      <0 0x1a004000 0 0x2000>,
+		      <0 0x1a006000 0 0x2000>;
+		reg-names = "camisp",
+		            "cam1",
+		            "cam2",
+		            "cam3";
+		interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+		interrupt-names = "cam1",
+				  "cam2",
+				  "cam3";
+		iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+			 <&iommu M4U_PORT_CAM_LSCI1>,
+			 <&iommu M4U_PORT_CAM_BPCI>;
+		#clock-cells = <1>;
+		power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
+		/* Camera CCF */
+		clocks = <&camsys CLK_CAM_CAM>,
+			 <&camsys CLK_CAM_CAMTG>;
+		clock-names = "CAMSYS_CAM_CGPDN",
+			      "CAMSYS_CAMTG_CGPDN";
+		mediatek,larb = <&larb3>,
+				<&larb6>;
+		mediatek,scp = <&scp>;
+		memory-region = <&cam_mem_reserved>;
+	};
+
+Reserved memory specific DT entry (see reserved memory binding for more
+information):
+
+Example:
+SoC specific DT entry:
+
+	cam_mem_reserved: cam_mem_region {
+		compatible = "mediatek,reserve-memory-cam-smem";
+		no-map;
+		size = <0 0x01400000>; / *20 MB share mem size */
+		alignment = <0 0x1000>;
+		alloc-ranges = <0 0x40000000 0 0x10000000>;
+	};
+
+Mediatek ISP P1 supports a single port node with MIPI-CSI2 bus. It should
+contain one 'port' child node with child 'endpoint' node. Please refer to
+the bindings defined in Documentation/devicetree/bindings/media/video-interfaces.txt
+and Documentation/devicetree/bindings/media/mediatek-seninf.txt.
+
+Example:
+Board specific DT entry:
+
+	&camisp {
+		port@0 {
+			seninf_0: endpoint {
+				remote-endpoint = <&seninf_core>;
+			};
+		};
+	};
+
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,04/11] dts: arm64: mt8183: Add ISP Pass 1 nodes
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (5 preceding siblings ...)
  2019-05-10  1:57 ` [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-10  1:57 ` [RFC, V2, 05/11] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Jungo Lin
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

Add nodes for Pass 1 unit of Mediatek's camera ISP system.
Pass 1 unit embedded in Mediatek SoCs, works with the
co-processor to process image signal from the image sensor
and output RAW image data.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 32 ++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index d5d83a05f8a1..5f195236a762 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -382,5 +382,37 @@
 			reg = <0 0x1a000000 0 0x1000>;
 			#clock-cells = <1>;
 		};
+
+		camisp: camisp@1a000000 {
+			compatible = "mediatek,mt8183-camisp", "syscon";
+			reg = <0 0x1a000000 0 0x1000>,
+			      <0 0x1a003000 0 0x1000>,
+			      <0 0x1a004000 0 0x2000>,
+			      <0 0x1a006000 0 0x2000>;
+			reg-names = "camisp",
+				    "cam1",
+				    "cam2",
+				    "cam3";
+			interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+			interrupt-names = "cam1",
+					  "cam2",
+					  "cam3";
+			iommus = <&iommu M4U_PORT_CAM_LSCI0>,
+				 <&iommu M4U_PORT_CAM_LSCI1>,
+				 <&iommu M4U_PORT_CAM_BPCI>;
+			#clock-cells = <1>;
+			power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
+			/* Camera CCF */
+			clocks = <&camsys CLK_CAM_CAM>,
+				 <&camsys CLK_CAM_CAMTG>;
+			clock-names = "CAMSYS_CAM_CGPDN",
+				      "CAMSYS_CAMTG_CGPDN";
+			mediatek,larb = <&larb3>,
+					<&larb6>;
+			mediatek,scp = <&scp>;
+			memory-region = <&cam_mem_reserved>;
+		};
 	};
 };
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 05/11] media: platform: Add Mediatek ISP Pass 1 driver Kconfig
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (6 preceding siblings ...)
  2019-05-10  1:57 ` [RFC,V2,04/11] dts: arm64: mt8183: Add ISP Pass 1 nodes Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-10  1:57 ` [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats Jungo Lin
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds Kconfig for Pass 1 (P1) unit driver of Mediatek's
camera ISP system. ISP P1 unit is embedded in Mediatek SoCs. It
provides RAW processing which includes optical black correction,
defect pixel correction, W/IR imbalance correction and lens
shading correction.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 drivers/media/platform/Kconfig         |  2 ++
 drivers/media/platform/mtk-isp/Kconfig | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/Kconfig

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 4acbed189644..7be62e06573b 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -32,6 +32,8 @@ source "drivers/media/platform/davinci/Kconfig"
 
 source "drivers/media/platform/omap/Kconfig"
 
+source "drivers/media/platform/mtk-isp/Kconfig"
+
 config VIDEO_ASPEED
 	tristate "Aspeed AST2400 and AST2500 Video Engine driver"
 	depends on VIDEO_V4L2
diff --git a/drivers/media/platform/mtk-isp/Kconfig b/drivers/media/platform/mtk-isp/Kconfig
new file mode 100644
index 000000000000..9932563d34c1
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/Kconfig
@@ -0,0 +1,21 @@
+config VIDEO_MEDIATEK_ISP_PASS1_SUPPORT
+	bool "Mediatek pass 1 image processing function"
+
+	select DMA_SHARED_BUFFER
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_V4L2
+	select VIDEOBUF2_MEMOPS
+	select VIDEOBUF2_VMALLOC
+	select MEDIA_CONTROLLER
+
+	default n
+	help
+		Pass 1 driver controls 3A (autofocus, exposure,
+		and white balance) with tuning parameters and outputs
+		the capture image buffers in Mediatek's camera system.
+
+		Choose y if you want to use Mediatek SoCs to create image
+		capture application such as video recording and still image
+		capture.
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (7 preceding siblings ...)
  2019-05-10  1:57 ` [RFC, V2, 05/11] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Jungo Lin
@ 2019-05-10  1:57 ` Jungo Lin
  2019-05-13  8:35   ` Hans Verkuil
  2019-05-10  1:58 ` [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control Jungo Lin
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:57 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

Add packed/unpacked/full-g bayer format with 8/10/12/14 bit
for image output. Add Pass 1 (P1) specific meta formats for
parameter processing and 3A/other statistics.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 include/uapi/linux/videodev2.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 1db220da3bcc..b79046d2d812 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -711,6 +711,20 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
 #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
 
+/* Vendor specific - Mediatek ISP compressed formats */
+#define V4L2_PIX_FMT_MTISP_U8	v4l2_fourcc('M', 'T', 'U', '8') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U10  v4l2_fourcc('M', 'T', 'U', 'A') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U12  v4l2_fourcc('M', 'T', 'U', 'C') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_U14  v4l2_fourcc('M', 'T', 'U', 'E') /* Unpacked bayer format, 16-bit */
+#define V4L2_PIX_FMT_MTISP_B8	v4l2_fourcc('M', 'T', 'B', '8') /* Packed   bayer format,  8-bit */
+#define V4L2_PIX_FMT_MTISP_B10  v4l2_fourcc('M', 'T', 'B', 'A') /* Packed   bayer format, 10-bit */
+#define V4L2_PIX_FMT_MTISP_B12  v4l2_fourcc('M', 'T', 'B', 'C') /* Packed   bayer format, 12-bit */
+#define V4L2_PIX_FMT_MTISP_B14  v4l2_fourcc('M', 'T', 'B', 'E') /* Packed   bayer format, 14-bit */
+#define V4L2_PIX_FMT_MTISP_F8	v4l2_fourcc('M', 'T', 'F', '8') /* Full-G   bayer format,  8-bit */
+#define V4L2_PIX_FMT_MTISP_F10  v4l2_fourcc('M', 'T', 'F', 'A') /* Full-G   bayer format, 10-bit */
+#define V4L2_PIX_FMT_MTISP_F12  v4l2_fourcc('M', 'T', 'F', 'C') /* Full-G   bayer format, 12-bit */
+#define V4L2_PIX_FMT_MTISP_F14  v4l2_fourcc('M', 'T', 'F', 'E') /* Full-G   bayer format, 14-bit */
+
 /* SDR formats - used only for Software Defined Radio devices */
 #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
 #define V4L2_SDR_FMT_CU16LE       v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
@@ -732,6 +746,12 @@ 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 */
 #define V4L2_META_FMT_D4XX        v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
+/* Vendor specific - Mediatek ISP parameters for firmware */
+#define V4L2_META_FMT_MTISP_PARAMS v4l2_fourcc('M', 'T', 'f', 'p') /* ISP tuning parameters */
+#define V4L2_META_FMT_MTISP_3A	   v4l2_fourcc('M', 'T', 'f', 'a') /* AE/AWB histogram */
+#define V4L2_META_FMT_MTISP_AF	   v4l2_fourcc('M', 'T', 'f', 'f') /* AF histogram */
+#define V4L2_META_FMT_MTISP_LCS	   v4l2_fourcc('M', 'T', 'f', 'c') /* Local contrast enhanced statistics */
+#define V4L2_META_FMT_MTISP_LMV	   v4l2_fourcc('M', 'T', 'f', 'm') /* Local motion vector histogram */
 
 /* priv field value to indicates that subsequent fields are valid. */
 #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (8 preceding siblings ...)
  2019-05-10  1:57 ` [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats Jungo Lin
@ 2019-05-10  1:58 ` Jungo Lin
  2019-05-13  8:46   ` Hans Verkuil
  2019-05-10  1:58 ` [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions Jungo Lin
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:58 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

Reserved Mediatek ISP P1 private control number with 16.
Moreover, add two private controls for ISP P1 user space
usage.

1. V4L2_CID_PRIVATE_GET_BIN_INFO
- Provide the image output width & height in case
camera binning mode is enabled.

2. V4L2_CID_PRIVATE_RAW_PATH
- Export the path control of the main stream to user space.
One is pure raw and the other is processing raw.
The default image path is pure raw.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c         | 133 ++++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h         |  32 +++++
 include/uapi/linux/v4l2-controls.h            |   4 +
 3 files changed, 169 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
new file mode 100644
index 000000000000..520adbe367ed
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include "mtk_cam-dev.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam.h"
+
+static int handle_ctrl_get_bin_info(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	const unsigned int idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	struct v4l2_format *imgo_fmt = &cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+	unsigned int width, height;
+
+	width = imgo_fmt->fmt.pix_mp.width;
+	height = imgo_fmt->fmt.pix_mp.height;
+
+	dev_dbg(&cam_dev->pdev->dev, "Get bin info w*h:%d*%d",
+		width, height);
+
+	ctrl->val = (width << 16) | height;
+
+	return 0;
+}
+
+static int handle_ctrl_get_raw_path(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
+
+	ctrl->val = p1_dev->isp_ctx.isp_raw_path;
+
+	dev_dbg(&cam_dev->pdev->dev, "Get raw path:%d", ctrl->val);
+
+	return 0;
+}
+
+static int handle_ctrl_set_raw_path(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_dev *cam_dev = ctrl->priv;
+	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
+
+	p1_dev->isp_ctx.isp_raw_path = ctrl->val;
+	dev_dbg(&cam_dev->pdev->dev, "Set raw path:%d", ctrl->val);
+	return 0;
+}
+
+static int mtk_cam_dev_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_GET_BIN_INFO:
+		handle_ctrl_get_bin_info(ctrl);
+		break;
+	case V4L2_CID_PRIVATE_RAW_PATH:
+		handle_ctrl_get_raw_path(ctrl);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mtk_cam_dev_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_PRIVATE_RAW_PATH:
+		return handle_ctrl_set_raw_path(ctrl);
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct v4l2_ctrl_ops mtk_cam_dev_ctrl_ops = {
+	.g_volatile_ctrl = mtk_cam_dev_g_ctrl,
+	.s_ctrl = mtk_cam_dev_s_ctrl,
+};
+
+struct v4l2_ctrl_config mtk_cam_controls[] = {
+	{
+	.ops = &mtk_cam_dev_ctrl_ops,
+	.id = V4L2_CID_PRIVATE_GET_BIN_INFO,
+	.name = "MTK CAM GET BIN INFO",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = (IMG_MIN_WIDTH << 16) | IMG_MIN_HEIGHT,
+	.max = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
+	.step = 1,
+	.def = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
+	.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
+	},
+	{
+	.ops = &mtk_cam_dev_ctrl_ops,
+	.id = V4L2_CID_PRIVATE_RAW_PATH,
+	.name = "MTK CAM RAW PATH",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+	},
+};
+
+int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
+		      struct v4l2_ctrl_handler *hdl)
+{
+	unsigned int i;
+
+	/* Initialized HW controls, allow V4L2_CID_MTK_CAM_MAX ctrls */
+	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_CAM_MAX);
+	if (hdl->error) {
+		v4l2_ctrl_handler_free(hdl);
+		return hdl->error;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mtk_cam_controls); i++)
+		v4l2_ctrl_new_custom(hdl, &mtk_cam_controls[i], cam_dev);
+
+	dev_dbg(&cam_dev->pdev->dev, "%s done", __func__);
+	return 0;
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
new file mode 100644
index 000000000000..74a6538c81ac
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_CTRL_H__
+#define __MTK_CAM_CTRL_H__
+
+#include <media/v4l2-ctrls.h>
+
+#define V4L2_CID_MTK_CAM_PRIVATE_CAM  V4L2_CID_USER_MTK_CAM_BASE
+#define V4L2_CID_PRIVATE_GET_BIN_INFO \
+	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 1)
+#define V4L2_CID_PRIVATE_RAW_PATH \
+	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 2)
+
+#define V4L2_CID_MTK_CAM_MAX	16
+
+int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
+		      struct v4l2_ctrl_handler *hdl);
+
+#endif /* __MTK_CAM_CTRL_H__ */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 06479f2fb3ae..cbe8f5f7782b 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -192,6 +192,10 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
 
+/* The base for the mediatek ISP Pass 1 driver controls */
+/* We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_MTK_CAM_BASE		(V4L2_CID_USER_BASE + 0x10c0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (9 preceding siblings ...)
  2019-05-10  1:58 ` [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control Jungo Lin
@ 2019-05-10  1:58 ` Jungo Lin
  2019-05-24 18:49   ` Drew Davenport
  2019-05-10  1:58 ` [RFC,V2,09/11] media: platform: Add Mediatek ISP P1 device driver Jungo Lin
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:58 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

Implement standard V4L2 video driver that utilizes V4L2
and media framework APIs. In this driver, supports one media
device, one sub-device and six video devices during
initialization. Moreover, it also connects with sensor and
senif drivers with V4L2 async APIs.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
This patch dependeds on "media: support Mediatek sensor interface driver"[1].

ISP P1 sub-device communicates with seninf sub-device with CIO.

[1]. media: support Mediatek sensor interface driver
https://patchwork.kernel.org/cover/10852957/
---
---
 .../platform/mtk-isp/isp_50/cam/Makefile      |   19 +
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.c |  758 ++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-dev.h |  250 ++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c    | 1086 +++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h    |   43 +
 5 files changed, 2156 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
new file mode 100644
index 000000000000..5a581ab65945
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+mtk-cam-isp-objs += \
+	mtk_cam.o mtk_cam-dev.o \
+	mtk_cam-ctrl.o mtk_cam-scp.o \
+	mtk_cam-v4l2-util.o mtk_cam-smem-dev.o
+
+obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1_SUPPORT) += mtk-cam-isp.o
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
new file mode 100644
index 000000000000..dda8a7b161ee
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-dev.h"
+#include "mtk_cam-smem.h"
+#include "mtk_cam-v4l2-util.h"
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
+	.vidioc_enum_fmt_vid_cap_mplane = mtk_cam_videoc_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = mtk_cam_videoc_g_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = mtk_cam_videoc_s_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = mtk_cam_videoc_try_fmt,
+	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
+	.vidioc_g_input = mtk_cam_vidioc_g_input,
+	.vidioc_s_input = mtk_cam_vidioc_s_input,
+	/* buffer queue management */
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_subscribe_event = mtk_cam_vidioc_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vout_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
+	.vidioc_enum_fmt_vid_out_mplane = mtk_cam_videoc_enum_fmt,
+	.vidioc_g_fmt_vid_out_mplane = mtk_cam_videoc_g_fmt,
+	.vidioc_s_fmt_vid_out_mplane = mtk_cam_videoc_s_fmt,
+	.vidioc_try_fmt_vid_out_mplane = mtk_cam_videoc_try_fmt,
+	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
+	.vidioc_g_input = mtk_cam_vidioc_g_input,
+	.vidioc_s_input = mtk_cam_vidioc_s_input,
+	/* buffer queue management */
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_fmt_meta_cap = mtk_cam_meta_enum_format,
+	.vidioc_g_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_s_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_try_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_videoc_querycap,
+	.vidioc_enum_fmt_meta_out = mtk_cam_meta_enum_format,
+	.vidioc_g_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_s_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_try_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static struct v4l2_format meta_fmts[] = {
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
+			.buffersize = 128 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_3A,
+			.buffersize = 300 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_AF,
+			.buffersize = 160 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_LCS,
+			.buffersize = 72 * PAGE_SIZE,
+		},
+	},
+	{
+		.fmt.meta = {
+			.dataformat = V4L2_META_FMT_MTISP_LMV,
+			.buffersize = 256,
+		},
+	},
+};
+
+/* Need to update mtk_cam_dev_fmt_set_img for default format configuration */
+static struct v4l2_format stream_out_fmts[] = {
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B8,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B12,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_B14,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.num_planes = 1,
+		},
+	},
+};
+
+static struct v4l2_format bin_out_fmts[] = {
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F8,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F12,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+	{
+		.fmt.pix_mp = {
+			.width = RRZ_MAX_WIDTH,
+			.height = RRZ_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_F14,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+			.num_planes = 1,
+		},
+	},
+};
+
+static const struct
+mtk_cam_dev_node_desc output_queues[MTK_CAM_P1_TOTAL_OUTPUT] = {
+	{
+		.id = MTK_CAM_P1_META_IN_0,
+		.name = "meta input",
+		.description = "ISP tuning parameters",
+		.cap = V4L2_CAP_META_OUTPUT,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.link_flags = 0,
+		.capture = false,
+		.image = false,
+		.smem_alloc = true,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 0,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
+	},
+};
+
+static const struct
+mtk_cam_dev_node_desc capture_queues[MTK_CAM_P1_TOTAL_CAPTURE] = {
+	{
+		.id = MTK_CAM_P1_MAIN_STREAM_OUT,
+		.name = "main stream",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = 0,
+		.capture = true,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = R_IMGO,
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_PACKED_BIN_OUT,
+		.name = "packed out",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = 0,
+		.capture = true,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = R_RRZO,
+		.fmts = bin_out_fmts,
+		.num_fmts = ARRAY_SIZE(bin_out_fmts),
+		.default_fmt_idx = 1,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_0,
+		.name = "partial meta 0",
+		.description = "AE/AWB histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = 0,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_AAO | R_FLKO | R_PSO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 1,
+		.max_buf_count = 5,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_1,
+		.name = "partial meta 1",
+		.description = "AF histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = 0,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_AFO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 2,
+		.max_buf_count = 5,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_2,
+		.name = "partial meta 2",
+		.description = "Local contrast enhanced statistics",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_DYNAMIC,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_LCSO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 3,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_CAM_P1_META_OUT_3,
+		.name = "partial meta 3",
+		.description = "Local motion vector histogram",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_DYNAMIC,
+		.capture = true,
+		.image = false,
+		.smem_alloc = false,
+		.dma_port = R_LMVO,
+		.fmts = meta_fmts,
+		.num_fmts = ARRAY_SIZE(meta_fmts),
+		.default_fmt_idx = 4,
+		.max_buf_count = 10,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+};
+
+static const struct mtk_cam_dev_queues_setting queues_setting = {
+	.output_node_descs = output_queues,
+	.total_output_nodes = MTK_CAM_P1_TOTAL_OUTPUT,
+	.capture_node_descs = capture_queues,
+	.total_capture_nodes = MTK_CAM_P1_TOTAL_CAPTURE,
+};
+
+static __u32 get_pixel_byte_by_fmt(__u32 pix_fmt)
+{
+	switch (pix_fmt) {
+	case V4L2_PIX_FMT_MTISP_B8:
+	case V4L2_PIX_FMT_MTISP_F8:
+		return 8;
+	case V4L2_PIX_FMT_MTISP_B10:
+	case V4L2_PIX_FMT_MTISP_F10:
+		return 10;
+	case V4L2_PIX_FMT_MTISP_B12:
+	case V4L2_PIX_FMT_MTISP_F12:
+		return 12;
+	case V4L2_PIX_FMT_MTISP_B14:
+	case V4L2_PIX_FMT_MTISP_F14:
+		return 14;
+	case V4L2_PIX_FMT_MTISP_U8:
+	case V4L2_PIX_FMT_MTISP_U10:
+	case V4L2_PIX_FMT_MTISP_U12:
+	case V4L2_PIX_FMT_MTISP_U14:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static __u32 align_main_stream_size(__u32 size, unsigned int pix_mode)
+{
+	switch (pix_mode) {
+	case default_pixel_mode:
+	case four_pixel_mode:
+		return ALIGN(size, 8);
+	case two_pixel_mode:
+		return ALIGN(size, 4);
+	case one_pixel_mode:
+		return ALIGN(size, 2);
+	default:
+		break;
+	}
+	return 0;
+}
+
+static unsigned int align_packetd_out_size(__u32 size,
+					   unsigned int pix_mode,
+					   __u32 fmt)
+{
+	switch (pix_mode) {
+	case default_pixel_mode:
+	case four_pixel_mode:
+		return ALIGN(size, 16);
+	case two_pixel_mode:
+		return ALIGN(size, 8);
+	case one_pixel_mode:
+		if (fmt == V4L2_PIX_FMT_MTISP_F10)
+			return ALIGN(size, 4);
+		else
+			return ALIGN(size, 8);
+	default:
+		return ALIGN(size, 16);
+	}
+	return 0;
+}
+
+static __u32 cal_main_stream_stride(struct device *dev,
+				    __u32 width,
+				    __u32 pix_fmt,
+				    __u32 pix_mode)
+{
+	__u32 stride;
+	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+	width = ALIGN(width, 4);
+	stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2);
+	/* expand stride, instead of shrink width */
+	stride = align_main_stream_size(stride, pix_mode);
+
+	dev_dbg(dev,
+		"main width:%d, pix_mode:%d, stride:%d\n",
+		width, pix_mode, stride);
+	return stride;
+}
+
+static __u32 cal_packed_out_stride(struct device *dev,
+				   __u32 width,
+				   __u32 pix_fmt,
+				   __u32 pix_mode)
+{
+	__u32 stride;
+	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
+
+	width = ALIGN(width, 4);
+	stride = DIV_ROUND_UP(width * 3, 2);
+	stride = DIV_ROUND_UP(stride * pixel_byte, 8);
+	/* expand stride, instead of shrink width */
+	stride = align_packetd_out_size(stride, pix_mode, pix_fmt);
+
+	dev_dbg(dev,
+		"packed width:%d, pix_mode:%d, stride:%d\n",
+		width, pix_mode, stride);
+
+	return stride;
+}
+
+static __u32 cal_img_stride(struct device *dev,
+			    int node_id,
+			    __u32 width,
+			    __u32 pix_fmt)
+{
+	__u32 bpl;
+
+	/* Currently, only support one_pixel_mode */
+	if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT)
+		bpl = cal_main_stream_stride(dev, width, pix_fmt,
+					     one_pixel_mode);
+	else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT)
+		bpl = cal_packed_out_stride(dev, width, pix_fmt,
+					    one_pixel_mode);
+
+	/* For DIP HW constrained, it needs 4 byte alignment */
+	bpl = ALIGN(bpl, 4);
+
+	return bpl;
+}
+
+struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
+{
+	unsigned int i;
+	struct v4l2_format *dev_fmt;
+
+	for (i = 0; i < desc->num_fmts; i++) {
+		dev_fmt = &desc->fmts[i];
+		if (dev_fmt->fmt.pix_mp.pixelformat == format)
+			return dev_fmt;
+	}
+
+	return NULL;
+}
+
+/* The helper to configure the device context */
+void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
+			     const struct mtk_cam_dev_queues_setting *setting)
+{
+	unsigned int i, node_idx;
+
+	node_idx = 0;
+
+	/* Setup the output queue */
+	for (i = 0; i < setting->total_output_nodes; i++)
+		cam_dev->mem2mem2_nodes[node_idx++].desc =
+			setting->output_node_descs[i];
+
+	/* Setup the capture queue */
+	for (i = 0; i < setting->total_capture_nodes; i++)
+		cam_dev->mem2mem2_nodes[node_idx++].desc =
+			setting->capture_node_descs[i];
+
+	cam_dev->dev_node_num = node_idx;
+}
+
+int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
+			   struct mtk_cam_dev_finish_param *fram_param)
+{
+	struct mtk_cam_dev_buffer *buf, *b0;
+
+	if (!cam_dev->streaming)
+		return 0;
+
+	dev_dbg(&cam_dev->pdev->dev,
+		"job recvied request fd:%d, frame_seq:%d state:%d\n",
+		fram_param->request_fd,
+		fram_param->frame_seq_no,
+		fram_param->state);
+
+	/*
+	 * Set the buffer's VB2 status so that the user can dequeue
+	 * the buffer.
+	 */
+	list_for_each_entry_safe(buf, b0, fram_param->list_buf, list) {
+		list_del(&buf->list);
+		buf->vbb.vb2_buf.timestamp = ktime_get_ns();
+		buf->vbb.sequence = fram_param->frame_seq_no;
+		if (buf->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+			vb2_buffer_done(&buf->vbb.vb2_buf, fram_param->state);
+	}
+
+	return 0;
+}
+
+int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
+				 __u32 frame_seq_no)
+{
+	struct v4l2_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = V4L2_EVENT_FRAME_SYNC;
+	event.u.frame_sync.frame_sequence = frame_seq_no;
+	v4l2_event_queue(cam_dev->subdev.devnode, &event);
+
+	return 0;
+}
+
+/* Calcuate mplane pix format */
+void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
+				    struct v4l2_pix_format_mplane *dest_fmt,
+				    unsigned int node_id)
+{
+	unsigned int i;
+	__u32 bpl, sizeimage, imagsize;
+
+	imagsize = 0;
+	for (i = 0 ; i < dest_fmt->num_planes; ++i) {
+		bpl = cal_img_stride(dev,
+				     node_id,
+				     dest_fmt->width,
+				     dest_fmt->pixelformat);
+		sizeimage = bpl * dest_fmt->height;
+		imagsize += sizeimage;
+		dest_fmt->plane_fmt[i].bytesperline = bpl;
+		dest_fmt->plane_fmt[i].sizeimage = sizeimage;
+		memset(dest_fmt->plane_fmt[i].reserved,
+		       0, sizeof(dest_fmt->plane_fmt[i].reserved));
+		dev_dbg(dev, "plane:%d,bpl:%d,sizeimage:%u\n",
+			i,  bpl, dest_fmt->plane_fmt[i].sizeimage);
+	}
+
+	if (dest_fmt->num_planes == 1)
+		dest_fmt->plane_fmt[0].sizeimage = imagsize;
+}
+
+void mtk_cam_dev_fmt_set_img(struct device *dev,
+			     struct v4l2_pix_format_mplane *dest_fmt,
+			     struct v4l2_pix_format_mplane *src_fmt,
+			     unsigned int node_id)
+{
+	dest_fmt->width = src_fmt->width;
+	dest_fmt->height = src_fmt->height;
+	dest_fmt->pixelformat = src_fmt->pixelformat;
+	dest_fmt->field = src_fmt->field;
+	dest_fmt->colorspace = src_fmt->colorspace;
+	dest_fmt->num_planes = src_fmt->num_planes;
+	/* Use default */
+	dest_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	dest_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+	dest_fmt->xfer_func =
+		V4L2_MAP_XFER_FUNC_DEFAULT(dest_fmt->colorspace);
+	memset(dest_fmt->reserved, 0, sizeof(dest_fmt->reserved));
+
+	dev_dbg(dev, "%s: Dest Fmt:%c%c%c%c, w*h:%d*%d\n",
+		__func__,
+		(dest_fmt->pixelformat & 0xFF),
+		(dest_fmt->pixelformat >> 8) & 0xFF,
+		(dest_fmt->pixelformat >> 16) & 0xFF,
+		(dest_fmt->pixelformat >> 24) & 0xFF,
+		dest_fmt->width,
+		dest_fmt->height);
+
+	mtk_cam_dev_cal_mplane_pix_fmt(dev, dest_fmt, node_id);
+}
+
+/* Get the default format setting */
+void mtk_cam_dev_load_default_fmt(struct device *dev,
+				  struct mtk_cam_dev_node_desc *queue_desc,
+				  struct v4l2_format *dest)
+{
+	struct v4l2_format *default_fmt =
+		&queue_desc->fmts[queue_desc->default_fmt_idx];
+
+	dest->type = queue_desc->buf_type;
+
+	/* Configure default format based on node type */
+	if (queue_desc->image) {
+		mtk_cam_dev_fmt_set_img(dev,
+					&dest->fmt.pix_mp,
+					&default_fmt->fmt.pix_mp,
+					queue_desc->id);
+	} else {
+		dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
+		dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
+	}
+}
+
+/* Get a free buffer from a video node */
+static struct mtk_cam_dev_buffer *
+mtk_cam_dev_get_pending_buf(struct mtk_cam_dev *cam_dev, int node)
+{
+	struct mtk_cam_dev_buffer *buf;
+	struct mtk_cam_video_device *vdev;
+
+	if (node > cam_dev->dev_node_num || node < 0) {
+		dev_err(&cam_dev->pdev->dev, "Invalid mtk_cam_dev node.\n");
+		return NULL;
+	}
+	vdev = &cam_dev->mem2mem2_nodes[node];
+
+	spin_lock(&vdev->slock);
+	buf = list_first_entry_or_null(&vdev->pending_list,
+				       struct mtk_cam_dev_buffer,
+				       list);
+	if (!buf) {
+		spin_unlock(&vdev->slock);
+		return NULL;
+	}
+	list_del(&buf->list);
+	spin_unlock(&vdev->slock);
+
+	return buf;
+}
+
+int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev)
+{
+	unsigned int node;
+	const int mtk_cam_dev_node_num = cam_dev->dev_node_num;
+	struct device *dev = &cam_dev->pdev->dev;
+	struct mtk_cam_dev_start_param s_param;
+	struct mtk_cam_dev_buffer *buf;
+
+	memset(&s_param, 0, sizeof(struct mtk_cam_dev_start_param));
+
+	if (!cam_dev->streaming) {
+		dev_dbg(dev, "%s: stream off, no enqueue\n", __func__);
+		return 0;
+	}
+
+	/* Check all enabled nodes to collect its buffer  */
+	for (node = 0; node < mtk_cam_dev_node_num; node++) {
+		if (!cam_dev->mem2mem2_nodes[node].enabled)
+			continue;
+		buf = mtk_cam_dev_get_pending_buf(cam_dev, node);
+		if (!buf)
+			continue;
+
+		/* TBD: use buf_init callback function */
+		buf->daddr =
+			vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
+		if (cam_dev->mem2mem2_nodes[node].desc.smem_alloc) {
+			buf->scp_addr = mtk_cam_smem_iova_to_scp_addr(
+				cam_dev->smem_dev, buf->daddr);
+		} else {
+			buf->scp_addr = 0;
+		}
+
+		dev_dbg(dev,
+			"Node:%d fd:%d idx:%d state:%d daddr:%pad addr:%pad",
+			node,
+			buf->vbb.request_fd,
+			buf->vbb.vb2_buf.index,
+			buf->vbb.vb2_buf.state,
+			&buf->daddr,
+			&buf->scp_addr);
+
+		s_param.buffers[node] = buf;
+		s_param.request_fd = buf->vbb.request_fd;
+	}
+
+	/* Trigger en-queued job to driver */
+	mtk_isp_req_enqueue(dev, &s_param);
+
+	return 0;
+}
+
+int mtk_cam_dev_init(struct platform_device *pdev,
+		     struct mtk_cam_dev *cam_dev)
+{
+	int ret;
+
+	cam_dev->pdev = pdev;
+
+	mtk_cam_dev_queue_setup(cam_dev, &queues_setting);
+
+	/* v4l2 sub-device registration */
+	dev_dbg(&cam_dev->pdev->dev, "mem2mem2.name: %s\n",
+		MTK_CAM_DEV_P1_NAME);
+
+	ret = mtk_cam_mem2mem2_v4l2_register(cam_dev);
+	if (ret)
+		return ret;
+
+	ret = mtk_cam_v4l2_async_register(cam_dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int mtk_cam_dev_release(struct platform_device *pdev,
+			struct mtk_cam_dev *cam_dev)
+{
+	mtk_cam_v4l2_async_unregister(cam_dev);
+	mtk_cam_v4l2_unregister(cam_dev);
+
+	return 0;
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
new file mode 100644
index 000000000000..410460de44fa
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#ifndef __MTK_CAM_DEV_H__
+#define __MTK_CAM_DEV_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#define MTK_CAM_DEV_P1_NAME		"MTK-ISP-P1-V4L2"
+
+#define MTK_CAM_DEV_NODES		11
+
+#define MTK_CAM_P1_META_IN_0		0
+#define MTK_CAM_P1_TOTAL_OUTPUT		1
+
+#define MTK_CAM_P1_MAIN_STREAM_OUT	1
+#define MTK_CAM_P1_PACKED_BIN_OUT	2
+#define MTK_CAM_P1_META_OUT_0		3
+#define MTK_CAM_P1_META_OUT_1		4
+#define MTK_CAM_P1_META_OUT_2		5
+#define MTK_CAM_P1_META_OUT_3		6
+#define MTK_CAM_P1_TOTAL_CAPTURE	6
+
+struct mtk_cam_dev_buffer {
+	struct vb2_v4l2_buffer	vbb;
+	struct list_head	list;
+	/* Intenal part */
+	dma_addr_t		daddr;
+	dma_addr_t		scp_addr;
+};
+
+/* Attributes setup by device owner */
+struct mtk_cam_dev_queues_setting {
+	const struct mtk_cam_dev_node_desc *output_node_descs;
+	unsigned int total_output_nodes;
+	const struct mtk_cam_dev_node_desc *capture_node_descs;
+	unsigned int total_capture_nodes;
+};
+
+struct mtk_cam_dev_start_param {
+	int request_fd;
+	struct mtk_cam_dev_buffer *buffers[MTK_CAM_DEV_NODES];
+};
+
+struct mtk_cam_dev_finish_param {
+	int request_fd;
+	unsigned int frame_seq_no;
+	unsigned int state;
+	struct list_head *list_buf;
+};
+
+/*
+ * struct mtk_cam_dev_node_desc - node attributes
+ *
+ * @id:		 id of the context queue
+ * @name:	 media entity name
+ * @description: descritpion of node
+ * @cap:	 mapped to V4L2 capabilities
+ * @buf_type:	 mapped to V4L2 buffer type
+ * @dma_port:	 the dma port associated to the buffer
+ * @link_flags:	 default media link flags
+ * @smem_alloc:	 using the cam_smem_drv as alloc ctx or not
+ * @capture:	 true for capture queue (device to user)
+ *		 false for output queue (from user to device)
+ * @image:	 true for image node, false for meta node
+ * @num_fmts:	 the number of supported formats
+ * @default_fmt_idx: default format of this node
+ * @max_buf_count: maximum V4L2 buffer count
+ * @ioctl_ops:  mapped to v4l2_ioctl_ops
+ * @fmts:	supported format
+ *
+ */
+struct mtk_cam_dev_node_desc {
+	u8 id;
+	char *name;
+	char *description;
+	u32 cap;
+	u32 buf_type;
+	u32 dma_port;
+	u32 link_flags;
+	u8 smem_alloc:1;
+	u8 capture:1;
+	u8 image:1;
+	u8 num_fmts;
+	u8 default_fmt_idx;
+	u8 max_buf_count;
+	const struct v4l2_ioctl_ops *ioctl_ops;
+	struct v4l2_format *fmts;
+};
+
+/*
+ * struct mtk_cam_video_device - Mediatek video device structure.
+ *
+ * @id:		Id for mtk_cam_dev_node_desc or mem2mem2_nodes array
+ * @enabled:	Indicate the device is enabled or not
+ * @vdev_fmt:	The V4L2 format of video device
+ * @vdev_apd:	The media pad graph object of video device
+ * @vbq:	A videobuf queue of video device
+ * @desc:	The node attributes of video device
+ * @ctrl_handler:	The control handler of video device
+ * @pending_list:	List for pending buffers before enqueuing into driver
+ * @lock:	Serializes vb2 queue and video device operations.
+ * @slock:	Protect for pending_list.
+ *
+ */
+struct mtk_cam_video_device {
+	unsigned int id;
+	unsigned int enabled;
+	struct v4l2_format vdev_fmt;
+	struct video_device vdev;
+	struct media_pad vdev_pad;
+	struct vb2_queue vbq;
+	struct mtk_cam_dev_node_desc desc;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct list_head pending_list;
+	/* Used for vbq & vdev */
+	struct mutex lock;
+	/* protect for pending_list */
+	spinlock_t slock;
+};
+
+/*
+ * struct mtk_cam_dev - Mediatek camera device structure.
+ *
+ * @pdev:	Pointer to platform device
+ * @smem_pdev:	Pointer to shared memory platform device
+ * @pipeline:	Media pipeline information
+ * @media_dev:	Media device
+ * @subdev:	The V4L2 sub-device
+ * @v4l2_dev:	The V4L2 device driver
+ * @notifier:	The v4l2_device notifier data
+ * @subdev_pads: Pointer to the number of media pads of this sub-device
+ * @ctrl_handler: The control handler
+ * @mem2mem2_nodes: The array list of mtk_cam_video_device
+ * @seninf:	Pointer to the seninf sub-device
+ * @sensor:	Pointer to the active sensor V4L2 sub-device when streaming on
+ * @streaming:	Indicate the overall streaming status is on or off
+ * @dev_node_num: The number of supported V4L2 video device nodes
+ * @request_fd:	The file descriptor of request API
+ * @request_count: The buffer count of request API
+ *
+ * Below is the graph topology for Camera IO connection.
+ * sensor 1 (main) --> sensor IF --> P1 sub-device
+ * sensor 2 (sub)  -->
+ *
+ */
+struct mtk_cam_dev {
+	struct platform_device *pdev;
+	struct device *smem_dev;
+	struct media_pipeline pipeline;
+	struct media_device media_dev;
+	struct v4l2_subdev subdev;
+	struct v4l2_device v4l2_dev;
+	struct v4l2_async_notifier notifier;
+	struct media_pad *subdev_pads;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct mtk_cam_video_device mem2mem2_nodes[MTK_CAM_DEV_NODES];
+	struct v4l2_subdev *seninf;
+	struct v4l2_subdev *sensor;
+	unsigned int streaming;
+	unsigned int dev_node_num;
+	int request_fd;
+	unsigned int request_count;
+};
+
+int mtk_cam_dev_init(struct platform_device *pdev,
+		     struct mtk_cam_dev *cam_dev);
+int mtk_cam_v4l2_register(struct device *dev,
+			  struct media_device *media_dev,
+			  struct v4l2_device *v4l2_dev,
+			  struct v4l2_ctrl_handler *ctrl_handler);
+int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev);
+int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev);
+int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev);
+void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev);
+int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev);
+int mtk_cam_dev_release(struct platform_device *pdev,
+			struct mtk_cam_dev *cam_dev);
+void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
+			     const struct mtk_cam_dev_queues_setting *setting);
+int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
+			   struct mtk_cam_dev_finish_param *param);
+int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
+				 __u32 frame_seq_no);
+void mtk_cam_dev_fmt_set_img(struct device *dev,
+			     struct v4l2_pix_format_mplane *dest_fmt,
+			     struct v4l2_pix_format_mplane *src_fmt,
+			     unsigned int node_id);
+struct v4l2_format *
+mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *queue_desc, u32 format);
+void mtk_cam_dev_load_default_fmt(struct device *dev,
+				  struct mtk_cam_dev_node_desc *queue,
+				  struct v4l2_format *dest_fmt);
+void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
+				    struct v4l2_pix_format_mplane *dest_fmt,
+				    unsigned int node_id);
+
+static inline struct mtk_cam_video_device *
+file_to_mtk_cam_node(struct file *__file)
+{
+	return container_of(video_devdata(__file),
+		struct mtk_cam_video_device, vdev);
+}
+
+static inline struct mtk_cam_dev *
+mtk_cam_subdev_to_dev(struct v4l2_subdev *__sd)
+{
+	return container_of(__sd,
+		struct mtk_cam_dev, subdev);
+}
+
+static inline struct mtk_cam_video_device *
+mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
+{
+	return container_of(__vq,
+		struct mtk_cam_video_device, vbq);
+}
+
+static inline struct mtk_cam_dev_buffer *
+mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
+{
+	return container_of(__vb,
+		struct mtk_cam_dev_buffer, vbb.vb2_buf);
+}
+
+#endif /* __MTK_CAM_DEV_H__ */
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
new file mode 100644
index 000000000000..196aaef3d854
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
@@ -0,0 +1,1086 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Mediatek Corporation.
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * MTK_CAM-v4l2 is highly based on Intel IPU3 ImgU driver.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-common.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam-dev.h"
+#include "mtk_cam-v4l2-util.h"
+
+#define MTK_CAM_SENINF_PAD_SRC			4
+#define MTK_CAM_P1_HUB_PAD_SINK			MTK_CAM_DEV_NODES
+
+static int mtk_cam_subdev_open(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_fh *fh)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	cam_dev->request_fd = -1;
+	cam_dev->request_count = 0;
+
+	return mtk_isp_open(&cam_dev->pdev->dev);
+}
+
+static int mtk_cam_subdev_close(struct v4l2_subdev *sd,
+				struct v4l2_subdev_fh *fh)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	return mtk_isp_release(&cam_dev->pdev->dev);
+}
+
+static int mtk_cam_v4l2_get_active_sensor(struct mtk_cam_dev *cam_dev)
+{
+	struct media_device *mdev = cam_dev->seninf->entity.graph_obj.mdev;
+	struct media_entity *entity;
+	struct device *dev = &cam_dev->pdev->dev;
+
+	cam_dev->sensor = NULL;
+	media_device_for_each_entity(entity, mdev) {
+		dev_dbg(dev, "media entity: %s:0x%x\n",
+			entity->name, entity->function);
+		if (entity->function == MEDIA_ENT_F_CAM_SENSOR &&
+		    entity->stream_count > 0) {
+			cam_dev->sensor = media_entity_to_v4l2_subdev(entity);
+			dev_dbg(dev, "Sensor found: %s\n", entity->name);
+			break;
+		}
+	}
+
+	if (!cam_dev->sensor) {
+		dev_err(dev, "Sensor is not connected\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	int ret;
+
+	/* Align vb2_core_streamon design */
+	if (cam_dev->streaming) {
+		dev_warn(dev, "already streaming\n", dev);
+		return 0;
+	}
+
+	if (!cam_dev->seninf) {
+		dev_err(dev, "no seninf connected:%d\n", ret);
+		return -EPERM;
+	}
+
+	/* Get active sensor from graph topology */
+	ret = mtk_cam_v4l2_get_active_sensor(cam_dev);
+	if (ret)
+		return -EPERM;
+
+	ret = mtk_isp_config(dev);
+	if (ret)
+		return -EPERM;
+
+	/* Seninf must stream on first */
+	dev_dbg(dev, "streamed on: %s\n", cam_dev->seninf->entity.name);
+	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);
+	if (ret) {
+		dev_err(dev, "%s stream on failed:%d\n",
+			cam_dev->seninf->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev, "streamed on: %s\n", cam_dev->sensor->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 1);
+	if (ret) {
+		dev_err(dev, "%s stream on failed:%d\n",
+			cam_dev->sensor->entity.name, ret);
+		goto fail_sensor_on;
+	}
+
+	cam_dev->streaming = true;
+	mtk_cam_dev_queue_req_buffers(cam_dev);
+	isp_composer_stream(isp_ctx, 1);
+	dev_dbg(dev, "streamed on Pass 1\n");
+
+	return 0;
+
+fail_sensor_on:
+	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
+	return -EPERM;
+}
+
+static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	int ret;
+
+	if (!cam_dev->streaming) {
+		dev_warn(dev, "already stream off");
+		return 0;
+	}
+
+	dev_dbg(dev, "stream off: %s\n", cam_dev->sensor->entity.name);
+	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0);
+	if (ret) {
+		dev_err(dev, "%s stream off failed:%d\n",
+			cam_dev->sensor->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev, "stream off: %s\n", cam_dev->seninf->entity.name);
+	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
+	if (ret) {
+		dev_err(dev, "%s stream off failed:%d\n",
+			cam_dev->seninf->entity.name, ret);
+		goto fail_sensor_off;
+	}
+
+	isp_composer_stream(isp_ctx, 0);
+	cam_dev->streaming = false;
+	dev_dbg(dev, "streamed off Pass 1\n");
+
+	return 0;
+
+fail_sensor_off:
+	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);
+	return -EPERM;
+}
+
+static int mtk_cam_subdev_s_stream(struct v4l2_subdev *sd,
+				   int enable)
+{
+	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
+
+	if (enable)
+		return mtk_cam_cio_stream_on(cam_dev);
+	else
+		return mtk_cam_cio_stream_off(cam_dev);
+}
+
+static int mtk_cam_subdev_subscribe_event(struct v4l2_subdev *subdev,
+					  struct v4l2_fh *fh,
+					  struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_FRAME_SYNC:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mtk_cam_link_setup(struct media_entity *entity,
+			      const struct media_pad *local,
+	const struct media_pad *remote, u32 flags)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(entity, struct mtk_cam_dev, subdev.entity);
+	u32 pad = local->index;
+
+	dev_dbg(&cam_dev->pdev->dev, "link setup: %d -> %d\n",
+		pad, remote->index);
+
+	if (pad < cam_dev->dev_node_num)
+		cam_dev->mem2mem2_nodes[pad].enabled =
+			!!(flags & MEDIA_LNK_FL_ENABLED);
+
+	return 0;
+}
+
+static void mtk_cam_dev_queue_buffers(struct vb2_buffer *vb)
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct mtk_cam_dev_buffer *buf;
+
+	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	buf->daddr = vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
+	buf->scp_addr = 0;
+
+	dev_dbg(&cam_dev->pdev->dev, "%pad:%pad\n",
+		&buf->daddr, &buf->scp_addr);
+
+	mtk_isp_enqueue(&cam_dev->pdev->dev, node->desc.dma_port, buf);
+}
+
+static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct mtk_cam_dev *mtk_cam_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+	struct device *dev = &mtk_cam_dev->pdev->dev;
+	struct mtk_cam_dev_buffer *buf;
+	struct vb2_v4l2_buffer *v4l2_buf;
+
+	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+	v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	dev_dbg(dev, "%s: node:%d fd:%d idx:%d\n",
+		__func__,
+		node->id,
+		v4l2_buf->request_fd,
+		v4l2_buf->vb2_buf.index);
+
+	if (v4l2_buf->request_fd < 0) {
+		mtk_cam_dev_queue_buffers(vb);
+		return;
+	}
+
+	if (mtk_cam_dev->request_fd != v4l2_buf->request_fd) {
+		mtk_cam_dev->request_fd = v4l2_buf->request_fd;
+		mtk_cam_dev->request_count =
+			vb->req_obj.req->num_incomplete_objects;
+		dev_dbg(dev, "init  mtk_cam_dev_buf, fd(%d) count(%d)\n",
+			v4l2_buf->request_fd,
+			vb->req_obj.req->num_incomplete_objects);
+	}
+
+	/* Added the buffer into the tracking list */
+	spin_lock(&node->slock);
+	list_add_tail(&buf->list, &node->pending_list);
+	spin_unlock(&node->slock);
+
+	mtk_cam_dev->request_count--;
+
+	if (!mtk_cam_dev->request_count) {
+		mtk_cam_dev->request_fd = -1;
+		mtk_cam_dev_queue_req_buffers(mtk_cam_dev);
+	}
+}
+
+static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
+				   unsigned int *num_buffers,
+				   unsigned int *num_planes,
+				   unsigned int sizes[],
+				   struct device *alloc_devs[])
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	struct device *dev = &cam_dev->pdev->dev;
+	unsigned int max_buffer_count = node->desc.max_buf_count;
+	const struct v4l2_format *fmt = &node->vdev_fmt;
+	unsigned int size;
+
+	/* Check the limitation of buffer size */
+	if (max_buffer_count > 0)
+		*num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
+	else
+		*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
+
+	if (node->desc.smem_alloc) {
+		alloc_devs[0] = cam_dev->smem_dev;
+		dev_dbg(dev, "Select smem alloc_devs(0x%pK)\n", alloc_devs[0]);
+	} else {
+		alloc_devs[0] = &cam_dev->pdev->dev;
+		dev_dbg(dev, "Select default alloc_devs(0x%pK)\n",
+			alloc_devs[0]);
+	}
+
+	if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
+	    vq->type == V4L2_BUF_TYPE_META_CAPTURE)
+		size = fmt->fmt.meta.buffersize;
+	else
+		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	/* Validate initialized num_planes & size[0] */
+	if (*num_planes) {
+		if (sizes[0] < size)
+			return -EINVAL;
+	} else {
+		*num_planes = 1;
+		sizes[0] = size;
+	}
+
+	/* Initialize buffer queue & locks */
+	INIT_LIST_HEAD(&node->pending_list);
+	mutex_init(&node->lock);
+	spin_lock_init(&node->slock);
+
+	return 0;
+}
+
+static bool
+mtk_cam_all_nodes_streaming(struct mtk_cam_dev *cam_dev,
+			    struct mtk_cam_video_device *except)
+{
+	unsigned int i;
+
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
+
+		if (node == except)
+			continue;
+		if (node->enabled && !vb2_start_streaming_called(&node->vbq))
+			return false;
+	}
+
+	return true;
+}
+
+static void mtk_cam_return_all_buffers(struct mtk_cam_dev *cam_dev,
+				       struct mtk_cam_video_device *node,
+				       enum vb2_buffer_state state)
+{
+	struct mtk_cam_dev_buffer *b, *b0;
+	unsigned int i;
+
+	dev_dbg(&cam_dev->pdev->dev, "%s: node:%s", __func__, node->vdev.name);
+
+	/* Return all buffers */
+	spin_lock(&node->slock);
+	list_for_each_entry_safe(b, b0, &node->pending_list, list) {
+		list_del(&b->list);
+	}
+	spin_unlock(&node->slock);
+
+	for (i = 0; i < node->vbq.num_buffers; ++i)
+		if (node->vbq.bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+			vb2_buffer_done(node->vbq.bufs[i], state);
+}
+
+static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
+				       unsigned int count)
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+	int ret;
+
+	if (!node->enabled) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d is not enable\n",
+			node->id);
+		ret = -ENOLINK;
+		goto fail_return_bufs;
+	}
+
+	ret = media_pipeline_start(&node->vdev.entity, &cam_dev->pipeline);
+	if (ret < 0) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d %s failed\n",
+			node->id, __func__);
+		goto fail_return_bufs;
+	}
+
+	if (!mtk_cam_all_nodes_streaming(cam_dev, node))
+		return 0;
+
+	/* Start streaming of the whole pipeline now */
+	ret = v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 1);
+	if (ret < 0) {
+		dev_err(&cam_dev->pdev->dev, "Node:%d s_stream failed\n",
+			node->id);
+		goto fail_stop_pipeline;
+	}
+	return 0;
+
+fail_stop_pipeline:
+	media_pipeline_stop(&node->vdev.entity);
+fail_return_bufs:
+	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_QUEUED);
+	return ret;
+}
+
+static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
+	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
+
+	/* Was this the first node with streaming disabled? */
+	if (mtk_cam_all_nodes_streaming(cam_dev, node)) {
+		/* Yes, really stop streaming now */
+		if (v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 0))
+			dev_err(&cam_dev->pdev->dev,
+				"failed to stop streaming\n");
+	}
+	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_ERROR);
+	media_pipeline_stop(&node->vdev.entity);
+}
+
+int mtk_cam_videoc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+
+	strscpy(cap->driver, MTK_CAM_DEV_P1_NAME, sizeof(cap->driver));
+	strscpy(cap->card, MTK_CAM_DEV_P1_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 dev_name(cam_dev->media_dev.dev));
+
+	return 0;
+}
+
+int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->index >= node->desc.num_fmts || f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat;
+	f->flags = 0;
+
+	return 0;
+}
+
+int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->fmt = node->vdev_fmt.fmt;
+
+	return 0;
+}
+
+int mtk_cam_videoc_try_fmt(struct file *file, void *fh,
+			   struct v4l2_format *in_fmt)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+	struct v4l2_format *dev_fmt;
+	__u32  width, height;
+
+	if (in_fmt->type != node->vbq.type)
+		return -EINVAL;
+
+	dev_dbg(&cam_dev->pdev->dev, "%s: fmt:%c%c%c%c, w*h:%u*%u\n",
+		__func__,
+		(in_fmt->fmt.pix_mp.pixelformat & 0xFF),
+		(in_fmt->fmt.pix_mp.pixelformat >> 8) & 0xFF,
+		(in_fmt->fmt.pix_mp.pixelformat >> 16) & 0xFF,
+		(in_fmt->fmt.pix_mp.pixelformat >> 24) & 0xFF,
+		in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height);
+
+	width = in_fmt->fmt.pix_mp.width;
+	height = in_fmt->fmt.pix_mp.height;
+
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc,
+				       in_fmt->fmt.pix_mp.pixelformat);
+	if (dev_fmt) {
+		mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
+					&in_fmt->fmt.pix_mp,
+					&dev_fmt->fmt.pix_mp,
+					node->id);
+	} else {
+		mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
+					     &node->desc,
+					     in_fmt);
+	}
+	in_fmt->fmt.pix_mp.width = clamp_t(u32,
+					   width,
+					   CAM_MIN_WIDTH,
+					   in_fmt->fmt.pix_mp.width);
+	in_fmt->fmt.pix_mp.height = clamp_t(u32,
+					    height,
+					    CAM_MIN_HEIGHT,
+					    in_fmt->fmt.pix_mp.height);
+	mtk_cam_dev_cal_mplane_pix_fmt(&cam_dev->pdev->dev,
+				       &in_fmt->fmt.pix_mp,
+				       node->id);
+
+	return 0;
+}
+
+int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct mtk_cam_dev *cam_dev = video_drvdata(file);
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	if (cam_dev->streaming)
+		return -EBUSY;
+
+	/* Get the valid format */
+	mtk_cam_videoc_try_fmt(file, fh, f);
+
+	/* Configure to video device */
+	mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
+				&node->vdev_fmt.fmt.pix_mp,
+				&f->fmt.pix_mp,
+				node->id);
+
+	return 0;
+}
+
+int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
+			      struct v4l2_input *input)
+{
+	if (input->index > 0)
+		return -EINVAL;
+
+	strscpy(input->name, "camera", sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	*input = 0;
+
+	return 0;
+}
+
+int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	return input == 0 ? 0 : -EINVAL;
+}
+
+int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
+				   const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+int mtk_cam_enum_framesizes(struct file *filp, void *priv,
+			    struct v4l2_frmsizeenum *sizes)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
+	struct v4l2_format *dev_fmt;
+
+	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
+	if (!dev_fmt || sizes->index)
+		return -EINVAL;
+
+	if (node->id == MTK_CAM_P1_MAIN_STREAM_OUT) {
+		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+		sizes->stepwise.max_width = IMG_MAX_WIDTH;
+		sizes->stepwise.min_width = IMG_MIN_WIDTH;
+		sizes->stepwise.max_height = IMG_MAX_HEIGHT;
+		sizes->stepwise.min_height = IMG_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	} else if (node->id == MTK_CAM_P1_PACKED_BIN_OUT) {
+		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+		sizes->stepwise.max_width = RRZ_MAX_WIDTH;
+		sizes->stepwise.min_width = RRZ_MIN_WIDTH;
+		sizes->stepwise.max_height = RRZ_MAX_HEIGHT;
+		sizes->stepwise.min_height = RRZ_MIN_HEIGHT;
+		sizes->stepwise.step_height = 1;
+		sizes->stepwise.step_width = 1;
+	}
+
+	return 0;
+}
+
+int mtk_cam_meta_enum_format(struct file *file, void *fh,
+			     struct v4l2_fmtdesc *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	/* Each node is dedicated to only one meta format */
+	if (f->index > 0 || f->type != node->vbq.type)
+		return -EINVAL;
+
+	strscpy(f->description, node->desc.description,
+		sizeof(node->desc.description));
+	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
+
+	return 0;
+}
+
+int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f)
+{
+	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
+
+	/* Each node is dedicated to only one meta format */
+	if (f->type != node->vbq.type)
+		return -EINVAL;
+
+	f->fmt = node->vdev_fmt.fmt;
+
+	return 0;
+}
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops mtk_cam_subdev_internal_ops = {
+	.open = mtk_cam_subdev_open,
+	.close = mtk_cam_subdev_close,
+};
+
+static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = {
+	.subscribe_event = mtk_cam_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = {
+	.s_stream = mtk_cam_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_ops mtk_cam_subdev_ops = {
+	.core = &mtk_cam_subdev_core_ops,
+	.video = &mtk_cam_subdev_video_ops,
+};
+
+static const struct media_entity_operations mtk_cam_media_ops = {
+	.link_setup = mtk_cam_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static void mtk_cam_vb2_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_cam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req,
+				   dev->v4l2_dev.ctrl_handler);
+}
+
+static const struct vb2_ops mtk_cam_vb2_ops = {
+	.buf_queue = mtk_cam_vb2_buf_queue,
+	.queue_setup = mtk_cam_vb2_queue_setup,
+	.start_streaming = mtk_cam_vb2_start_streaming,
+	.stop_streaming = mtk_cam_vb2_stop_streaming,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.buf_request_complete = mtk_cam_vb2_buf_request_complete,
+};
+
+static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
+	.unlocked_ioctl = video_ioctl2,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.poll = vb2_fop_poll,
+	.mmap = vb2_fop_mmap,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = v4l2_compat_ioctl32,
+#endif
+};
+
+/*
+ * Config node's video properties
+ * according to the device context requirement
+ */
+static void mtk_cam_node_to_v4l2(struct mtk_cam_dev *cam_dev,
+				 unsigned int node,
+				 struct video_device *vdev,
+				 struct v4l2_format *f)
+{
+	struct mtk_cam_dev_node_desc *node_desc =
+		&cam_dev->mem2mem2_nodes[node].desc;
+
+	/* set cap/type/ioctl_ops of the video device */
+	vdev->device_caps = V4L2_CAP_STREAMING | node_desc->cap;
+	f->type = node_desc->buf_type;
+	vdev->ioctl_ops = node_desc->ioctl_ops;
+
+	mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
+				     node_desc,
+				     f);
+}
+
+static const struct media_device_ops mtk_cam_media_req_ops = {
+	.req_validate = vb2_request_validate,
+	.req_queue = vb2_request_queue,
+};
+
+static int mtk_cam_media_register(struct device *dev,
+				  struct media_device *media_dev)
+{
+	int ret;
+
+	media_dev->dev = dev;
+	strscpy(media_dev->model, MTK_CAM_DEV_P1_NAME,
+		sizeof(media_dev->model));
+	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+		 "platform:%s", dev_name(dev));
+	media_dev->hw_revision = 0;
+	media_device_init(media_dev);
+	media_dev->ops = &mtk_cam_media_req_ops;
+	dev_info(dev, "Register media device: %s, 0x%pK",
+		 MTK_CAM_DEV_P1_NAME, media_dev);
+
+	ret = media_device_register(media_dev);
+	if (ret) {
+		dev_err(dev, "failed to register media device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_cam_v4l2_register(struct device *dev,
+			  struct media_device *media_dev,
+			  struct v4l2_device *v4l2_dev,
+			  struct v4l2_ctrl_handler *ctrl_handler)
+{
+	int ret;
+
+	/* Set up v4l2 device */
+	v4l2_dev->ctrl_handler = ctrl_handler;
+	v4l2_dev->mdev = media_dev;
+	dev_info(dev, "Register v4l2 device: 0x%pK", v4l2_dev);
+	ret = v4l2_device_register(dev, v4l2_dev);
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	return 0;
+
+fail_v4l2_dev:
+	media_device_unregister(media_dev);
+	media_device_cleanup(media_dev);
+
+	return ret;
+}
+
+int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev)
+{
+	struct device *dev = &cam_dev->pdev->dev;
+	unsigned int num_nodes = cam_dev->dev_node_num;
+	/* Total pad numbers is video devices + one seninf pad */
+	unsigned int num_subdev_pads = MTK_CAM_DEV_NODES + 1;
+	unsigned int i;
+	int ret;
+
+	ret = mtk_cam_media_register(dev,
+				     &cam_dev->media_dev);
+	if (ret) {
+		dev_err(dev, "failed to register media device:%d\n", ret);
+		goto fail_media_dev;
+	}
+
+	ret = mtk_cam_v4l2_register(dev,
+				    &cam_dev->media_dev,
+				    &cam_dev->v4l2_dev,
+				    NULL);
+	if (ret) {
+		dev_err(dev, "failed to register V4L2 device:%d\n", ret);
+		goto fail_v4l2_dev;
+	}
+
+	/* Initialize subdev media entity */
+	cam_dev->subdev_pads = devm_kcalloc(dev, num_subdev_pads,
+					    sizeof(*cam_dev->subdev_pads),
+					    GFP_KERNEL);
+	if (!cam_dev->subdev_pads) {
+		ret = -ENOMEM;
+		goto fail_subdev_pads;
+	}
+
+	ret = media_entity_pads_init(&cam_dev->subdev.entity,
+				     num_subdev_pads,
+				     cam_dev->subdev_pads);
+	if (ret) {
+		dev_err(dev, "failed initialize media pads:%d:\n", ret);
+		goto fail_subdev_pads;
+	}
+
+	/* Initialize all pads with MEDIA_PAD_FL_SOURCE */
+	for (i = 0; i < num_subdev_pads; i++)
+		cam_dev->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+	/* Customize the last one pad as CIO sink pad. */
+	cam_dev->subdev_pads[MTK_CAM_DEV_NODES].flags = MEDIA_PAD_FL_SINK;
+
+	/* Initialize subdev */
+	v4l2_subdev_init(&cam_dev->subdev, &mtk_cam_subdev_ops);
+	cam_dev->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
+	cam_dev->subdev.entity.ops = &mtk_cam_media_ops;
+	cam_dev->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
+				V4L2_SUBDEV_FL_HAS_EVENTS;
+	snprintf(cam_dev->subdev.name, sizeof(cam_dev->subdev.name),
+		 "%s", MTK_CAM_DEV_P1_NAME);
+	v4l2_set_subdevdata(&cam_dev->subdev, cam_dev);
+	cam_dev->subdev.internal_ops = &mtk_cam_subdev_internal_ops;
+
+	dev_info(dev, "register subdev: %s\n", cam_dev->subdev.name);
+	ret = v4l2_device_register_subdev(&cam_dev->v4l2_dev, &cam_dev->subdev);
+	if (ret) {
+		dev_err(dev, "failed initialize subdev:%d\n", ret);
+		goto fail_subdev;
+	}
+
+	/* Create video nodes and links */
+	for (i = 0; i < num_nodes; i++) {
+		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
+		struct video_device *vdev = &node->vdev;
+		struct vb2_queue *vbq = &node->vbq;
+		u32 output = !cam_dev->mem2mem2_nodes[i].desc.capture;
+		u32 link_flags = cam_dev->mem2mem2_nodes[i].desc.link_flags;
+
+		cam_dev->subdev_pads[i].flags = output ?
+			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+		/* Initialize miscellaneous variables */
+		mutex_init(&node->lock);
+		spin_lock_init(&node->slock);
+		INIT_LIST_HEAD(&node->pending_list);
+
+		/* Initialize formats to default values */
+		mtk_cam_node_to_v4l2(cam_dev, i, vdev, &node->vdev_fmt);
+
+		/* Initialize media entities */
+		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
+		if (ret) {
+			dev_err(dev, "failed initialize media pad:%d\n", ret);
+			goto fail_vdev_media_entity;
+		}
+		node->enabled = false;
+		node->id = i;
+		node->vdev_pad.flags = cam_dev->subdev_pads[i].flags;
+		vdev->entity.ops = NULL;
+
+		/* Initialize vbq */
+		vbq->type = node->vdev_fmt.type;
+		if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT)
+			vbq->io_modes = VB2_MMAP;
+		else
+			vbq->io_modes = VB2_MMAP | VB2_DMABUF;
+		if (node->desc.smem_alloc)
+			vbq->bidirectional = 1;
+		if (vbq->type == V4L2_BUF_TYPE_META_CAPTURE)
+			vdev->entity.function =
+				MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
+		vbq->ops = &mtk_cam_vb2_ops;
+		vbq->mem_ops = &vb2_dma_contig_memops;
+		vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer);
+		vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+		vbq->min_buffers_needed = 0;	/* Can streamon w/o buffers */
+		/* Put the process hub sub device in the vb2 private data */
+		vbq->drv_priv = cam_dev;
+		vbq->lock = &node->lock;
+		vbq->supports_requests = true;
+
+		ret = vb2_queue_init(vbq);
+		if (ret) {
+			dev_err(dev, "failed to init. vb2 queue:%d\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Initialize vdev */
+		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
+			 MTK_CAM_DEV_P1_NAME, node->desc.name);
+		vdev->release = video_device_release_empty;
+		vdev->fops = &mtk_cam_v4l2_fops;
+		vdev->lock = &node->lock;
+		vdev->v4l2_dev = &cam_dev->v4l2_dev;
+		vdev->queue = &node->vbq;
+		vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
+		/* Enable private control for image video devices */
+		if (node->desc.image) {
+			mtk_cam_ctrl_init(cam_dev, &node->ctrl_handler);
+			vdev->ctrl_handler = &node->ctrl_handler;
+		}
+		video_set_drvdata(vdev, cam_dev);
+		dev_dbg(dev, "register vdev:%d:%s\n", i, vdev->name);
+
+		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+		if (ret) {
+			dev_err(dev, "failed to register vde:%d\n", ret);
+			goto fail_vdev;
+		}
+
+		/* Create link between video node and the subdev pad */
+		if (output) {
+			ret = media_create_pad_link(&vdev->entity, 0,
+						    &cam_dev->subdev.entity,
+						    i, link_flags);
+		} else {
+			ret = media_create_pad_link(&cam_dev->subdev.entity,
+						    i, &vdev->entity, 0,
+						    link_flags);
+		}
+		if (ret)
+			goto fail_link;
+	}
+
+	vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
+
+	return 0;
+
+	for (; i >= 0; i--) {
+fail_link:
+		video_unregister_device(&cam_dev->mem2mem2_nodes[i].vdev);
+fail_vdev:
+		media_entity_cleanup(&cam_dev->mem2mem2_nodes[i].vdev.entity);
+fail_vdev_media_entity:
+		mutex_destroy(&cam_dev->mem2mem2_nodes[i].lock);
+	}
+fail_subdev:
+	media_entity_cleanup(&cam_dev->subdev.entity);
+fail_subdev_pads:
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+fail_v4l2_dev:
+fail_media_dev:
+	dev_err(dev, "fail_v4l2_dev mdev: 0x%pK:%d", &cam_dev->media_dev, ret);
+	media_device_unregister(&cam_dev->media_dev);
+	media_device_cleanup(&cam_dev->media_dev);
+
+	return ret;
+}
+
+int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev)
+{
+	unsigned int i;
+	struct mtk_cam_video_device *dev;
+
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		dev = &cam_dev->mem2mem2_nodes[i];
+		video_unregister_device(&dev->vdev);
+		media_entity_cleanup(&dev->vdev.entity);
+		mutex_destroy(&dev->lock);
+		if (dev->desc.image)
+			v4l2_ctrl_handler_free(&dev->ctrl_handler);
+	}
+
+	vb2_dma_contig_clear_max_seg_size(&cam_dev->pdev->dev);
+	v4l2_device_unregister_subdev(&cam_dev->subdev);
+	media_entity_cleanup(&cam_dev->subdev.entity);
+	kfree(cam_dev->subdev_pads);
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+	media_device_unregister(&cam_dev->media_dev);
+	media_device_cleanup(&cam_dev->media_dev);
+
+	return 0;
+}
+
+static int mtk_cam_dev_complete(struct v4l2_async_notifier *notifier)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+	struct device *dev = &cam_dev->pdev->dev;
+	int ret;
+
+	ret = media_create_pad_link(&cam_dev->seninf->entity,
+				    MTK_CAM_SENINF_PAD_SRC,
+				    &cam_dev->subdev.entity,
+				    MTK_CAM_P1_HUB_PAD_SINK,
+				    0);
+	if (ret)
+		dev_err(dev, "fail to create pad link %s %s err:%d\n",
+			cam_dev->seninf->entity.name,
+			cam_dev->subdev.entity.name,
+			ret);
+
+	dev_info(dev, "Complete the v4l2 registration\n");
+
+	ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "failed initialize subdev nodes:%d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier,
+				      struct v4l2_subdev *sd,
+				      struct v4l2_async_subdev *asd)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+
+	cam_dev->seninf = sd;
+	dev_info(&cam_dev->pdev->dev, "%s is bounded\n", sd->entity.name);
+	return 0;
+}
+
+static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier,
+					struct v4l2_subdev *sd,
+					struct v4l2_async_subdev *asd)
+{
+	struct mtk_cam_dev *cam_dev =
+		container_of(notifier, struct mtk_cam_dev, notifier);
+
+	dev_dbg(&cam_dev->pdev->dev, "%s is unbounded\n", sd->entity.name);
+}
+
+static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+	return mtk_cam_dev_complete(notifier);
+}
+
+static const struct v4l2_async_notifier_operations mtk_cam_async_ops = {
+	.bound = mtk_cam_dev_notifier_bound,
+	.unbind = mtk_cam_dev_notifier_unbind,
+	.complete = mtk_cam_dev_notifier_complete,
+};
+
+static int mtk_cam_dev_fwnode_parse(struct device *dev,
+				    struct v4l2_fwnode_endpoint *vep,
+				    struct v4l2_async_subdev *asd)
+{
+	return 0;
+}
+
+int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev)
+{
+	int ret;
+
+	ret = v4l2_async_notifier_parse_fwnode_endpoints(
+		&cam_dev->pdev->dev, &cam_dev->notifier,
+		sizeof(struct v4l2_async_subdev),
+		mtk_cam_dev_fwnode_parse);
+	if (ret < 0)
+		return ret;
+
+	if (!cam_dev->notifier.num_subdevs)
+		return -ENODEV;
+
+	cam_dev->notifier.ops = &mtk_cam_async_ops;
+	dev_info(&cam_dev->pdev->dev, "mtk_cam v4l2_async_notifier_register\n");
+	ret = v4l2_async_notifier_register(&cam_dev->v4l2_dev,
+					   &cam_dev->notifier);
+	if (ret) {
+		dev_err(&cam_dev->pdev->dev,
+			"failed to register async notifier : %d\n", ret);
+		v4l2_async_notifier_cleanup(&cam_dev->notifier);
+	}
+
+	return ret;
+}
+
+void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev)
+{
+	v4l2_async_notifier_unregister(&cam_dev->notifier);
+	v4l2_async_notifier_cleanup(&cam_dev->notifier);
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
new file mode 100644
index 000000000000..73b36916da08
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_DEV_V4L2_H__
+#define __MTK_CAM_DEV_V4L2_H__
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+int mtk_cam_videoc_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap);
+int mtk_cam_enum_framesizes(struct file *filp, void *priv,
+			    struct v4l2_frmsizeenum *sizes);
+int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
+			    struct v4l2_fmtdesc *f);
+int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int mtk_cam_videoc_try_fmt(struct file *file,
+			   void *fh, struct v4l2_format *in_fmt);
+int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
+			      struct v4l2_input *input);
+int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input);
+int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input);
+int mtk_cam_meta_enum_format(struct file *file, void *fh,
+			     struct v4l2_fmtdesc *f);
+int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
+			      struct v4l2_format *f);
+int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
+				   const struct v4l2_event_subscription *sub);
+
+#endif /* __MTK_CAM_DEV_V4L2_H__ */
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,V2,09/11] media: platform: Add Mediatek ISP P1 device driver
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (10 preceding siblings ...)
  2019-05-10  1:58 ` [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions Jungo Lin
@ 2019-05-10  1:58 ` Jungo Lin
  2019-05-24 21:19   ` [RFC, V2, 09/11] " Drew Davenport
  2019-05-10  1:58 ` [RFC, V2, 10/11] media: platform: Add Mediatek ISP P1 SCP communication Jungo Lin
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:58 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds the Mediatek ISP P1 HW control device driver.
It handles the ISP HW configuration, provides interrupt handling and
initializes the V4L2 device nodes and other functions.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../mtk-isp/isp_50/cam/mtk_cam-regs.h         |  149 ++
 .../platform/mtk-isp/isp_50/cam/mtk_cam.c     | 1206 +++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam.h     |  300 ++++
 3 files changed, 1655 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
new file mode 100644
index 000000000000..342f0e0e9837
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_REGS_H
+#define _CAM_REGS_H
+
+/* TG Bit Mask */
+#define VFDATA_EN_BIT	BIT(0)
+#define CMOS_EN_BIT	BIT(0)
+
+/* normal signal bit */
+#define VS_INT_ST	BIT(0)
+#define HW_PASS1_DON_ST	BIT(11)
+#define SOF_INT_ST	BIT(12)
+#define SW_PASS1_DON_ST	BIT(30)
+
+/* err status bit */
+#define TG_ERR_ST	BIT(4)
+#define TG_GBERR_ST	BIT(5)
+#define CQ_CODE_ERR_ST	BIT(6)
+#define CQ_APB_ERR_ST	BIT(7)
+#define CQ_VS_ERR_ST	BIT(8)
+#define AMX_ERR_ST	BIT(15)
+#define RMX_ERR_ST	BIT(16)
+#define BMX_ERR_ST	BIT(17)
+#define RRZO_ERR_ST	BIT(18)
+#define AFO_ERR_ST	BIT(19)
+#define IMGO_ERR_ST	BIT(20)
+#define AAO_ERR_ST	BIT(21)
+#define PSO_ERR_ST	BIT(22)
+#define LCSO_ERR_ST	BIT(23)
+#define BNR_ERR_ST	BIT(24)
+#define LSCI_ERR_ST	BIT(25)
+#define DMA_ERR_ST	BIT(29)
+
+/* CAM DMA done status */
+#define FLKO_DONE_ST	BIT(4)
+#define AFO_DONE_ST	BIT(5)
+#define AAO_DONE_ST	BIT(7)
+#define PSO_DONE_ST	BIT(14)
+
+/* IRQ signal mask */
+#define INT_ST_MASK_CAM	( \
+			VS_INT_ST |\
+			SOF_INT_ST |\
+			HW_PASS1_DON_ST |\
+			SW_PASS1_DON_ST)
+
+/* IRQ Warning Mask */
+#define INT_ST_MASK_CAM_WARN	(\
+				RRZO_ERR_ST |\
+				AFO_ERR_ST |\
+				IMGO_ERR_ST |\
+				AAO_ERR_ST |\
+				PSO_ERR_ST | \
+				LCSO_ERR_ST |\
+				BNR_ERR_ST |\
+				LSCI_ERR_ST)
+
+/* IRQ Error Mask */
+#define INT_ST_MASK_CAM_ERR	(\
+				TG_ERR_ST |\
+				TG_GBERR_ST |\
+				CQ_CODE_ERR_ST |\
+				CQ_APB_ERR_ST |\
+				CQ_VS_ERR_ST |\
+				BNR_ERR_ST |\
+				RMX_ERR_ST |\
+				BMX_ERR_ST |\
+				BNR_ERR_ST |\
+				LSCI_ERR_ST |\
+				DMA_ERR_ST)
+
+/* IRQ Signal Log Mask */
+#define INT_ST_LOG_MASK_CAM	(\
+				SOF_INT_ST |\
+				SW_PASS1_DON_ST |\
+				VS_INT_ST |\
+				TG_ERR_ST |\
+				TG_GBERR_ST |\
+				RRZO_ERR_ST |\
+				AFO_ERR_ST |\
+				IMGO_ERR_ST |\
+				AAO_ERR_ST |\
+				DMA_ERR_ST)
+
+/* DMA Event Notification Mask */
+#define DMA_ST_MASK_CAM	(\
+			AFO_DONE_ST |\
+			AAO_DONE_ST |\
+			PSO_DONE_ST |\
+			FLKO_DONE_ST)
+
+/* Status check */
+#define REG_CTL_EN		0x0004
+#define REG_CTL_DMA_EN		0x0008
+#define REG_CTL_FMT_SEL		0x0010
+#define REG_CTL_EN2		0x0018
+#define REG_CTL_RAW_INT_EN	0x0020
+#define REG_CTL_RAW_INT_STAT	0x0024
+#define REG_CTL_RAW_INT2_STAT	0x0034
+#define REG_CTL_RAW_INT3_STAT	0x00c4
+#define REG_CTL_TWIN_STAT	0x0050
+
+#define REG_TG_SEN_MODE		0x0230
+#define REG_TG_SEN_GRAB_PIX	0x0238
+#define REG_TG_SEN_GRAB_LIN	0x023c
+#define REG_TG_VF_CON		0x0234
+#define REG_TG_SUB_PERIOD	0x02a4
+
+#define REG_IMGO_BASE_ADDR	0x1020
+#define REG_RRZO_BASE_ADDR	0x1050
+
+/* Error status log */
+#define REG_IMGO_ERR_STAT	0x1360
+#define REG_RRZO_ERR_STAT	0x1364
+#define REG_AAO_ERR_STAT	0x1368
+#define REG_AFO_ERR_STAT	0x136c
+#define REG_LCSO_ERR_STAT	0x1370
+#define REG_UFEO_ERR_STAT	0x1374
+#define REG_PDO_ERR_STAT	0x1378
+#define REG_BPCI_ERR_STAT	0x137c
+#define REG_LSCI_ERR_STAT	0x1384
+#define REG_PDI_ERR_STAT	0x138c
+#define REG_LMVO_ERR_STAT	0x1390
+#define REG_FLKO_ERR_STAT	0x1394
+#define REG_PSO_ERR_STAT	0x13a0
+
+/* ISP command */
+#define REG_CQ_THR0_BASEADDR	0x0198
+#define REG_HW_FRAME_NUM	0x13b8
+
+/* META */
+#define REG_META0_VB2_INDEX	0x14dc
+#define REG_META1_VB2_INDEX	0x151c
+
+#endif	/* _CAM_REGS_H */
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
new file mode 100644
index 000000000000..fc874ec8f7f0
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
@@ -0,0 +1,1206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/sched/clock.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-regs.h"
+#include "mtk_cam-smem.h"
+
+static const struct of_device_id mtk_isp_of_ids[] = {
+	{.compatible = "mediatek,mt8183-camisp",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_isp_of_ids);
+
+/* list of clocks required by isp cam */
+static const char * const mtk_isp_clks[] = {
+	"CAMSYS_CAM_CGPDN", "CAMSYS_CAMTG_CGPDN"
+};
+
+static void isp_dump_dma_status(struct isp_device *isp_dev)
+{
+	dev_err(isp_dev->dev,
+		"IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n",
+		readl(isp_dev->regs + REG_IMGO_ERR_STAT),
+		readl(isp_dev->regs + REG_RRZO_ERR_STAT),
+		readl(isp_dev->regs + REG_AAO_ERR_STAT),
+		readl(isp_dev->regs + REG_AFO_ERR_STAT),
+		readl(isp_dev->regs + REG_LMVO_ERR_STAT));
+	dev_err(isp_dev->dev,
+		"LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n",
+		readl(isp_dev->regs + REG_LCSO_ERR_STAT),
+		readl(isp_dev->regs + REG_PSO_ERR_STAT),
+		readl(isp_dev->regs + REG_FLKO_ERR_STAT),
+		readl(isp_dev->regs + REG_BPCI_ERR_STAT),
+		readl(isp_dev->regs + REG_LSCI_ERR_STAT));
+}
+
+static void mtk_isp_notify(struct mtk_isp_p1_ctx *isp_ctx,
+			   unsigned int request_fd,
+			   unsigned int frame_seq_no,
+			   struct list_head *list_buf,
+			   enum vb2_buffer_state state)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_cam_dev_finish_param fram_param;
+
+	fram_param.list_buf = list_buf;
+	fram_param.request_fd = request_fd;
+	fram_param.frame_seq_no = frame_seq_no;
+	fram_param.state = state;
+	dev_dbg(dev, "request fd:%d frame_seq_no:%d\n",
+		fram_param.request_fd,
+		fram_param.frame_seq_no);
+	mtk_cam_dev_job_finish(p1_dev->cam_dev, &fram_param);
+}
+
+static void isp_deque_frame(struct isp_p1_device *p1_dev,
+			    unsigned int node_id, int vb2_index,
+			    int frame_seq_no)
+{
+	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
+	struct device *dev = &p1_dev->pdev->dev;
+	struct vb2_queue *vb2_queue = &cam_dev->mem2mem2_nodes[node_id].vbq;
+	struct vb2_buffer *vb;
+	struct vb2_v4l2_buffer *vbb;
+
+	if (!cam_dev->mem2mem2_nodes[node_id].enabled)
+		return;
+
+	mutex_lock(vb2_queue->lock);
+	list_for_each_entry(vb, &vb2_queue->queued_list, queued_entry) {
+		vbb = to_vb2_v4l2_buffer(vb);
+		if (vbb->request_fd < 0 &&
+		    vb->index == vb2_index &&
+		    vb->state == VB2_BUF_STATE_ACTIVE) {
+			dev_dbg(dev, "%s:%d:%d", __func__, node_id, vb2_index);
+			vbb->vb2_buf.timestamp = ktime_get_ns();
+			vbb->sequence = frame_seq_no;
+			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		}
+	}
+	mutex_unlock(vb2_queue->lock);
+}
+
+static void isp_deque_request_frame(struct isp_p1_device *p1_dev,
+				    int frame_seq_no)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_isp_queue_job *framejob, *tmp;
+	struct isp_queue *p1_enqueue_list = &isp_ctx->p1_enqueue_list;
+
+	/* Match dequeue work and enqueue frame */
+	spin_lock(&p1_enqueue_list->lock);
+	list_for_each_entry_safe(framejob, tmp, &p1_enqueue_list->queue,
+				 list_entry) {
+		dev_dbg(dev,
+			"%s frame_seq_no:%d, target frame_seq_no:%d\n",
+			__func__,
+			framejob->frame_seq_no, frame_seq_no);
+		/* Match by the en-queued request number */
+		if (framejob->frame_seq_no == frame_seq_no) {
+			/* Pass to user space */
+			mtk_isp_notify(isp_ctx,
+				       framejob->request_fd,
+				       framejob->frame_seq_no,
+				       &framejob->list_buf,
+				       VB2_BUF_STATE_DONE);
+			atomic_dec(&p1_enqueue_list->queue_cnt);
+			dev_dbg(dev,
+				"frame_seq_no:%d is done, queue_cnt:%d\n",
+				framejob->frame_seq_no,
+				atomic_read(&p1_enqueue_list->queue_cnt));
+
+			/* remove only when frame ready */
+			list_del(&framejob->list_entry);
+			kfree(framejob);
+			break;
+		} else if (framejob->frame_seq_no < frame_seq_no) {
+			/* Pass to user space for frame drop */
+			mtk_isp_notify(isp_ctx,
+				       framejob->request_fd,
+				       framejob->frame_seq_no,
+				       &framejob->list_buf,
+				       VB2_BUF_STATE_ERROR);
+			atomic_dec(&p1_enqueue_list->queue_cnt);
+			dev_dbg(dev,
+				"frame_seq_no:%d drop, queue_cnt:%d\n",
+				framejob->frame_seq_no,
+				atomic_read(&p1_enqueue_list->queue_cnt));
+
+			/* remove only drop frame */
+			list_del(&framejob->list_entry);
+			kfree(framejob);
+		}
+	}
+	spin_unlock(&p1_enqueue_list->lock);
+}
+
+static int isp_deque_work(void *data)
+{
+	struct isp_p1_device *p1_dev = (struct isp_p1_device *)data;
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
+	struct mtk_cam_dev_stat_event_data event_data;
+	atomic_t *irq_data_end = &isp_ctx->irq_data_end;
+	atomic_t *irq_data_start = &isp_ctx->irq_data_start;
+	unsigned long flags;
+	int ret, i;
+
+	while (1) {
+		ret = wait_event_interruptible(isp_ctx->isp_deque_thread.wq,
+					       (atomic_read(irq_data_end) !=
+					       atomic_read(irq_data_start)) ||
+					       kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
+		i = atomic_read(&isp_ctx->irq_data_start);
+		memcpy(&event_data, &isp_ctx->irq_event_datas[i],
+		       sizeof(event_data));
+		memset(&isp_ctx->irq_event_datas[i], 0x00, sizeof(event_data));
+		atomic_set(&isp_ctx->irq_data_start, ++i & 0x3);
+		spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
+
+		if (event_data.irq_status_mask & VS_INT_ST) {
+			/* Notify specific HW events to user space */
+			mtk_cam_dev_event_frame_sync(cam_dev,
+						     event_data.frame_seq_no);
+			dev_dbg(dev,
+				"event IRQ:0x%x DMA:0x%x is sent\n",
+				event_data.irq_status_mask,
+				event_data.dma_status_mask);
+		}
+
+		if (event_data.dma_status_mask & AAO_DONE_ST) {
+			isp_deque_frame(p1_dev,
+					MTK_CAM_P1_META_OUT_0,
+					event_data.meta0_vb2_index,
+					event_data.frame_seq_no);
+		}
+
+		if (event_data.irq_status_mask & SW_PASS1_DON_ST) {
+			isp_deque_frame(p1_dev,
+					MTK_CAM_P1_META_OUT_0,
+					event_data.meta0_vb2_index,
+					event_data.frame_seq_no);
+
+			isp_deque_request_frame(p1_dev,
+						event_data.frame_seq_no);
+		}
+	}
+	return 0;
+}
+
+static int irq_handle_sof(struct isp_device *isp_dev,
+			  dma_addr_t base_addr,
+			  unsigned int frame_num)
+{
+	unsigned int cq_addr_index;
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	int cq_num = atomic_read(&p1_dev->isp_ctx.composed_frame_id);
+
+	if (cq_num > frame_num) {
+		cq_addr_index = frame_num % CQ_BUFFER_COUNT;
+
+		writel(base_addr +
+			(dma_addr_t)(CQ_ADDRESS_OFFSET * cq_addr_index),
+			isp_dev->regs + REG_CQ_THR0_BASEADDR);
+		dev_dbg(isp_dev->dev,
+			"SOF_INT_ST, update next, cq_num:%d, frame_num:%d cq_addr:%d",
+			cq_num, frame_num, cq_addr_index);
+	} else {
+		dev_dbg(isp_dev->dev,
+			"SOF_INT_ST, wait next, cq_num:%d, frame_num:%d",
+			cq_num, frame_num);
+	}
+
+	isp_dev->sof_count += 1;
+
+	return cq_num;
+}
+
+static int irq_handle_notify_event(struct isp_device *isp_dev,
+				   unsigned int irqstatus,
+				   unsigned int dmastatus)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
+	i = atomic_read(&isp_ctx->irq_data_end);
+	isp_ctx->irq_event_datas[i].frame_seq_no = isp_dev->current_frame;
+	isp_ctx->irq_event_datas[i].meta0_vb2_index = isp_dev->meta0_vb2_index;
+	isp_ctx->irq_event_datas[i].meta1_vb2_index = isp_dev->meta1_vb2_index;
+	isp_ctx->irq_event_datas[i].irq_status_mask |=
+		(irqstatus & INT_ST_MASK_CAM);
+	isp_ctx->irq_event_datas[i].dma_status_mask |=
+		(dmastatus & DMA_ST_MASK_CAM);
+	atomic_set(&isp_ctx->irq_data_end, ++i & 0x3);
+	spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
+
+	wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
+
+	dev_dbg(isp_dev->dev,
+		"%s IRQ:0x%x DMA:0x%x seq:%d idx0:%d idx1:%d\n",
+		__func__,
+		(irqstatus & INT_ST_MASK_CAM),
+		(dmastatus & DMA_ST_MASK_CAM),
+		isp_dev->current_frame,
+		isp_dev->meta0_vb2_index,
+		isp_dev->meta1_vb2_index);
+
+	return 0;
+}
+
+irqreturn_t isp_irq_cam(int irq, void *data)
+{
+	struct isp_device *isp_dev = (struct isp_device *)data;
+	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = isp_dev->dev;
+	unsigned int cardinalnum, cq_num, hw_frame_num;
+	unsigned int meta0_vb2_index, meta1_vb2_index;
+	unsigned int irqstatus, errstatus, warnstatus, dmastatus;
+	unsigned long flags;
+
+	/* Check the streaming is off or not */
+	if (!p1_dev->cam_dev->streaming)
+		return IRQ_HANDLED;
+
+	cardinalnum = isp_dev->isp_hw_module - ISP_CAM_A_IDX;
+	cq_num = 0;
+
+	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
+	irqstatus = readl(isp_dev->regs + REG_CTL_RAW_INT_STAT);
+	dmastatus = readl(isp_dev->regs + REG_CTL_RAW_INT2_STAT);
+	hw_frame_num = readl(isp_dev->regs + REG_HW_FRAME_NUM);
+	meta0_vb2_index = readl(isp_dev->regs + REG_META0_VB2_INDEX);
+	meta1_vb2_index = readl(isp_dev->regs + REG_META1_VB2_INDEX);
+	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
+
+	/* Ignore unnecessary IRQ */
+	if (irqstatus == 0)
+		return IRQ_HANDLED;
+
+	errstatus = irqstatus & INT_ST_MASK_CAM_ERR;
+	warnstatus = irqstatus & INT_ST_MASK_CAM_WARN;
+	irqstatus = irqstatus & INT_ST_MASK_CAM;
+
+	/* sof , done order check . */
+	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
+	if ((irqstatus & HW_PASS1_DON_ST) && (irqstatus & SOF_INT_ST)) {
+		dev_warn(dev,
+			 "isp sof_don block, sof_cnt:%d\n",
+			 isp_dev->sof_count);
+
+		/* Notify IRQ event and enqueue ready frame */
+		irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
+		isp_dev->current_frame = hw_frame_num;
+		isp_dev->meta0_vb2_index = meta0_vb2_index;
+		isp_dev->meta1_vb2_index = meta1_vb2_index;
+	} else {
+		if (irqstatus & SOF_INT_ST) {
+			isp_dev->current_frame = hw_frame_num;
+			isp_dev->meta0_vb2_index = meta0_vb2_index;
+			isp_dev->meta1_vb2_index = meta1_vb2_index;
+		}
+
+		if ((irqstatus & INT_ST_MASK_CAM) ||
+		    (dmastatus & DMA_ST_MASK_CAM))
+			irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
+	}
+	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
+
+	if (irqstatus & SOF_INT_ST)
+		cq_num = irq_handle_sof(isp_dev, isp_ctx->scp_mem_iova,
+					hw_frame_num);
+
+	if (irqstatus & SW_PASS1_DON_ST) {
+		int num = atomic_dec_return(&isp_ctx->composing_frame);
+
+		dev_dbg(dev, "SW_PASS1_DON_ST queued frame:%d\n", num);
+		/* Notify TX thread to send if TX frame is blocked */
+		wake_up_interruptible
+				(&isp_ctx->composer_tx_thread.wq);
+	}
+
+	/* check ISP error status */
+	if (errstatus) {
+		dev_err(dev,
+			"raw_int_err:0x%x/0x%x/0x%x\n",
+			irqstatus, warnstatus, errstatus);
+
+		/* show DMA errors in detail */
+		if (errstatus & DMA_ERR_ST)
+			isp_dump_dma_status(isp_dev);
+	}
+
+	if (irqstatus & INT_ST_LOG_MASK_CAM)
+		dev_dbg(dev, IRQ_STAT_STR,
+			'A' + cardinalnum,
+			isp_dev->sof_count,
+			irqstatus,
+			dmastatus,
+			hw_frame_num,
+			cq_num);
+	return IRQ_HANDLED;
+}
+
+static int enable_sys_clock(struct isp_p1_device *p1_dev)
+{
+	struct device *dev = &p1_dev->pdev->dev;
+	int ret;
+
+	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
+
+	ret = clk_bulk_prepare_enable(p1_dev->isp_clk.num_clks,
+				      p1_dev->isp_clk.clk_list);
+	if (ret < 0)
+		goto clk_err;
+	return 0;
+clk_err:
+	dev_err(dev, "cannot pre-en isp_cam clock:%d\n", ret);
+	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
+				   p1_dev->isp_clk.clk_list);
+	return ret;
+}
+
+static void disable_sys_clock(struct isp_p1_device *p1_dev)
+{
+	struct device *dev = &p1_dev->pdev->dev;
+
+	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
+	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
+				   p1_dev->isp_clk.clk_list);
+}
+
+static int mtk_isp_probe(struct platform_device *pdev)
+{
+	struct isp_p1_device *p1_dev;
+	struct mtk_isp_p1_ctx *isp_ctx;
+	struct isp_device *isp_dev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+	unsigned int i;
+
+	/* Allocate context */
+	p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL);
+	if (!p1_dev)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, p1_dev);
+	isp_ctx = &p1_dev->isp_ctx;
+	p1_dev->pdev = pdev;
+
+	p1_dev->isp_devs =
+		devm_kzalloc(dev,
+			     sizeof(struct isp_device) * ISP_DEV_NODE_NUM,
+			     GFP_KERNEL);
+	if (!p1_dev->isp_devs)
+		return -ENOMEM;
+
+	p1_dev->cam_dev =
+		devm_kzalloc(dev, sizeof(struct mtk_cam_dev), GFP_KERNEL);
+	if (!p1_dev->isp_devs)
+		return -ENOMEM;
+
+	/* iomap registers */
+	for (i = ISP_CAMSYS_CONFIG_IDX; i < ISP_DEV_NODE_NUM; i++) {
+		isp_dev = &p1_dev->isp_devs[i];
+		isp_dev->isp_hw_module = i;
+		isp_dev->dev = dev;
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		isp_dev->regs = devm_ioremap_resource(dev, res);
+
+		dev_info(dev, "cam%u, map_addr=0x%lx\n",
+			 i, (unsigned long)isp_dev->regs);
+
+		if (!isp_dev->regs)
+			return PTR_ERR(isp_dev->regs);
+
+		/* support IRQ from ISP_CAM_A_IDX */
+		if (i >= ISP_CAM_A_IDX) {
+			/* reg & interrupts index is shifted with 1  */
+			isp_dev->irq = platform_get_irq(pdev, i - 1);
+			if (isp_dev->irq > 0) {
+				ret = devm_request_irq(dev, isp_dev->irq,
+						       isp_irq_cam,
+						       IRQF_SHARED,
+						       dev_driver_string(dev),
+						       (void *)isp_dev);
+				if (ret) {
+					dev_err(dev,
+						"req_irq fail, dev:%s irq=%d\n",
+						dev->of_node->name,
+						isp_dev->irq);
+					return ret;
+				}
+				dev_info(dev, "Registered irq=%d, ISR:%s\n",
+					 isp_dev->irq, dev_driver_string(dev));
+			}
+		}
+		spin_lock_init(&isp_dev->spinlock_irq);
+	}
+
+	p1_dev->isp_clk.num_clks = ARRAY_SIZE(mtk_isp_clks);
+	p1_dev->isp_clk.clk_list =
+		devm_kcalloc(dev,
+			     p1_dev->isp_clk.num_clks,
+			     sizeof(*p1_dev->isp_clk.clk_list),
+			     GFP_KERNEL);
+	if (!p1_dev->isp_clk.clk_list)
+		return -ENOMEM;
+
+	for (i = 0; i < p1_dev->isp_clk.num_clks; ++i)
+		p1_dev->isp_clk.clk_list->id = mtk_isp_clks[i];
+
+	ret = devm_clk_bulk_get(dev,
+				p1_dev->isp_clk.num_clks,
+				p1_dev->isp_clk.clk_list);
+	if (ret) {
+		dev_err(dev, "cannot get isp cam clock:%d\n", ret);
+		return ret;
+	}
+
+	/* Initialize reserved DMA memory */
+	ret = mtk_cam_reserved_memory_init(p1_dev);
+	if (ret) {
+		dev_err(dev, "failed to configure DMA memory\n");
+		return ret;
+	}
+
+	/* Initialize the v4l2 common part */
+	ret = mtk_cam_dev_init(pdev, p1_dev->cam_dev);
+	if (ret)
+		return ret;
+
+	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
+	atomic_set(&p1_dev->isp_ctx.isp_user_cnt, 0);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+static int mtk_isp_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct isp_p1_device *p1_dev = dev_get_drvdata(dev);
+
+	pm_runtime_disable(dev);
+	mtk_cam_dev_release(pdev, p1_dev->cam_dev);
+
+	return 0;
+}
+
+static int mtk_isp_suspend(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct isp_device *isp_dev;
+	unsigned int reg_val;
+	int usercount, module;
+
+	module = p1_dev->isp_ctx.isp_hw_module;
+	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
+
+	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
+
+	/* If no user count, no further action */
+	if (!usercount)
+		return 0;
+
+	isp_dev = &p1_dev->isp_devs[module];
+	reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
+	if (reg_val & VFDATA_EN_BIT) {
+		dev_dbg(dev, "Cam:%d suspend, disable VF\n", module);
+		/* disable VF */
+		writel((reg_val & (~VFDATA_EN_BIT)),
+		       isp_dev->regs + REG_TG_VF_CON);
+		/*
+		 * After VF enable, The TG frame count will be reset to 0;
+		 */
+		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
+		writel((reg_val & (~CMOS_EN_BIT)),
+		       isp_dev->regs +  + REG_TG_SEN_MODE);
+	}
+
+	disable_sys_clock(p1_dev);
+
+	return 0;
+}
+
+static int mtk_isp_resume(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct isp_device *isp_dev;
+	unsigned int reg_val;
+	int module, usercount;
+
+	module = p1_dev->isp_ctx.isp_hw_module;
+	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
+
+	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
+
+	/* If no user count, no further action */
+	if (!usercount)
+		return 0;
+
+	enable_sys_clock(p1_dev);
+
+	/* V4L2 stream-on phase & restore HW stream-on status */
+	if (p1_dev->cam_dev->streaming) {
+		isp_dev = &p1_dev->isp_devs[module];
+		dev_dbg(dev, "Cam:%d resume,enable VF\n", module);
+		/* Enable CMOS */
+		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
+		writel((reg_val | CMOS_EN_BIT),
+		       isp_dev->regs + REG_TG_SEN_MODE);
+		/* Enable VF */
+		reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
+		writel((reg_val | VFDATA_EN_BIT),
+		       isp_dev->regs + REG_TG_VF_CON);
+	}
+	return 0;
+}
+
+static int isp_setup_scp_rproc(struct isp_p1_device *p1_dev)
+{
+	phandle rproc_phandle;
+	struct device *dev = &p1_dev->pdev->dev;
+	int ret;
+
+	p1_dev->scp_pdev = scp_get_pdev(p1_dev->pdev);
+	if (!p1_dev->scp_pdev) {
+		dev_err(dev, "Failed to get scp device\n");
+		return -ENODEV;
+	}
+	ret = of_property_read_u32(dev->of_node, "mediatek,scp",
+				   &rproc_phandle);
+	if (ret) {
+		dev_err(dev, "fail to get rproc_phandle:%d\n", ret);
+		return -EINVAL;
+	}
+
+	p1_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle);
+	dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n\n",
+		p1_dev->rproc_handle);
+	if (!p1_dev->rproc_handle) {
+		dev_err(dev, "fail to get rproc_handle\n");
+		return -EINVAL;
+	}
+
+	ret = rproc_boot(p1_dev->rproc_handle);
+	if (ret < 0) {
+		/*
+		 * Return 0 if downloading firmware successfully,
+		 * otherwise it is failed
+		 */
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int isp_init_context(struct isp_p1_device *p1_dev)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct device *dev = &p1_dev->pdev->dev;
+	unsigned int i;
+
+	dev_dbg(dev, "init irq work thread\n");
+	if (!isp_ctx->isp_deque_thread.thread) {
+		mutex_init(&isp_ctx->composer_tx_lock);
+		init_waitqueue_head(&isp_ctx->isp_deque_thread.wq);
+		isp_ctx->isp_deque_thread.thread =
+			kthread_run(isp_deque_work, (void *)p1_dev,
+				    "isp_deque_work");
+		if (IS_ERR(isp_ctx->isp_deque_thread.thread)) {
+			dev_err(dev, "unable to alloc kthread\n");
+			isp_ctx->isp_deque_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+	spin_lock_init(&isp_ctx->irq_dequeue_lock);
+
+	INIT_LIST_HEAD(&isp_ctx->p1_enqueue_list.queue);
+	atomic_set(&isp_ctx->p1_enqueue_list.queue_cnt, 0);
+
+	for (i = 0; i < ISP_DEV_NODE_NUM; i++)
+		spin_lock_init(&p1_dev->isp_devs[i].spinlock_irq);
+
+	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
+	spin_lock_init(&isp_ctx->composer_txlist.lock);
+
+	atomic_set(&isp_ctx->irq_data_end, 0);
+	atomic_set(&isp_ctx->irq_data_start, 0);
+	return 0;
+}
+
+static int isp_uninit_context(struct isp_p1_device *p1_dev)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct mtk_isp_queue_job *framejob, *tmp_framejob;
+
+	spin_lock_irq(&isp_ctx->p1_enqueue_list.lock);
+	list_for_each_entry_safe(framejob, tmp_framejob,
+				 &isp_ctx->p1_enqueue_list.queue, list_entry) {
+		list_del(&framejob->list_entry);
+		kfree(framejob);
+	}
+	spin_unlock_irq(&isp_ctx->p1_enqueue_list.lock);
+
+	atomic_set(&isp_ctx->isp_user_cnt, 0);
+
+	if (!IS_ERR(isp_ctx->isp_deque_thread.thread)) {
+		kthread_stop(isp_ctx->isp_deque_thread.thread);
+		wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
+		isp_ctx->isp_deque_thread.thread = NULL;
+	}
+
+	return 0;
+}
+
+static unsigned int get_enable_dma_ports(struct mtk_cam_dev *cam_dev)
+{
+	unsigned int enable_dma_ports, i;
+
+	/* Get the enabled meta DMA ports */
+	enable_dma_ports = 0;
+	for (i = 0; i < cam_dev->dev_node_num; i++) {
+		if (cam_dev->mem2mem2_nodes[i].enabled)
+			enable_dma_ports |=
+				cam_dev->mem2mem2_nodes[i].desc.dma_port;
+	}
+	dev_dbg(&cam_dev->pdev->dev, "%s enable_dma_ports:0x%x",
+		__func__, enable_dma_ports);
+
+	return enable_dma_ports;
+}
+
+/* Utility functions */
+static unsigned int get_sensor_pixel_id(unsigned int fmt)
+{
+	switch (fmt) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+		return raw_pxl_id_b;
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+		return raw_pxl_id_gb;
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+		return raw_pxl_id_gr;
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return raw_pxl_id_r;
+	default:
+		return raw_pxl_id_b;
+	}
+}
+
+static unsigned int get_sensor_fmt(unsigned int fmt)
+{
+	switch (fmt) {
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		return img_fmt_bayer8;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		return img_fmt_bayer10;
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		return img_fmt_bayer12;
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+		return img_fmt_bayer14;
+	default:
+		return img_fmt_unknown;
+	}
+}
+
+static unsigned int get_img_fmt(unsigned int fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_MTISP_B8:
+		return img_fmt_bayer8;
+	case V4L2_PIX_FMT_MTISP_F8:
+		return img_fmt_fg_bayer8;
+	case V4L2_PIX_FMT_MTISP_B10:
+		return img_fmt_bayer10;
+	case V4L2_PIX_FMT_MTISP_F10:
+		return img_fmt_fg_bayer10;
+	case V4L2_PIX_FMT_MTISP_B12:
+		return img_fmt_bayer12;
+	case V4L2_PIX_FMT_MTISP_F12:
+		return img_fmt_fg_bayer12;
+	case V4L2_PIX_FMT_MTISP_B14:
+		return img_fmt_bayer14;
+	case V4L2_PIX_FMT_MTISP_F14:
+		return img_fmt_fg_bayer14;
+	default:
+		return img_fmt_unknown;
+	}
+}
+
+static unsigned int get_pixel_byte(unsigned int fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_MTISP_B8:
+	case V4L2_PIX_FMT_MTISP_F8:
+		return 8;
+	case V4L2_PIX_FMT_MTISP_B10:
+	case V4L2_PIX_FMT_MTISP_F10:
+		return 10;
+	case V4L2_PIX_FMT_MTISP_B12:
+	case V4L2_PIX_FMT_MTISP_F12:
+		return 12;
+	case V4L2_PIX_FMT_MTISP_B14:
+	case V4L2_PIX_FMT_MTISP_F14:
+		return 14;
+	case V4L2_PIX_FMT_MTISP_U8:
+	case V4L2_PIX_FMT_MTISP_U10:
+	case V4L2_PIX_FMT_MTISP_U12:
+	case V4L2_PIX_FMT_MTISP_U14:
+		return 16;
+	default:
+		return 10;
+	}
+}
+
+static void composer_deinit_done_cb(void *data)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
+
+	disable_sys_clock(p1_dev);
+	/* Notify PM */
+	pm_runtime_put_sync(&p1_dev->pdev->dev);
+}
+
+/* ISP P1 interface functions */
+int mtk_isp_open(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	s32 usercount = atomic_inc_return(&isp_ctx->isp_user_cnt);
+	int ret;
+
+	dev_dbg(dev, "%s usercount=%d\n", __func__, usercount);
+
+	if (usercount == 1) {
+		ret = isp_setup_scp_rproc(p1_dev);
+		if (ret)
+			goto scp_err;
+
+		/* ISP HW INIT */
+		isp_ctx->isp_hw_module = ISP_CAM_B_IDX;
+		/* Use pure RAW as default HW path */
+		isp_ctx->isp_raw_path = ISP_PURE_RAW_PATH;
+		/* Check enabled DMAs which is configured by media setup */
+		isp_ctx->enable_dma_ports =
+			get_enable_dma_ports(p1_dev->cam_dev);
+
+		if (!isp_ctx->enable_dma_ports) {
+			dev_dbg(dev, "No DMAs are enabled\n");
+			ret = -EINVAL;
+			goto scp_err;
+		}
+
+		pm_runtime_get_sync(dev);
+
+		ret = isp_init_context(p1_dev);
+		if (ret)
+			goto ctx_err;
+		ret = isp_composer_init(isp_ctx);
+		if (ret)
+			goto composer_err;
+		ret = isp_composer_hw_init(isp_ctx);
+		if (ret)
+			goto composer_err;
+
+		isp_composer_meta_config(&p1_dev->isp_ctx,
+					 isp_ctx->enable_dma_ports);
+	}
+
+	return 0;
+composer_err:
+	isp_uninit_context(p1_dev);
+ctx_err:
+	pm_runtime_put_sync(dev);
+scp_err:
+	atomic_dec_return(&isp_ctx->isp_user_cnt);
+	return ret;
+}
+
+int mtk_isp_release(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+
+	if (atomic_dec_and_test(&p1_dev->isp_ctx.isp_user_cnt)) {
+		isp_composer_hw_deinit(isp_ctx, composer_deinit_done_cb);
+		isp_uninit_context(p1_dev);
+	}
+
+	dev_dbg(dev, "%s usercount=%d\n", __func__,
+		atomic_read(&p1_dev->isp_ctx.isp_user_cnt));
+
+	return 0;
+}
+
+int mtk_isp_config(struct device *dev)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct p1_config_param config_param;
+	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
+	struct v4l2_subdev_format sd_format;
+	unsigned int sd_width, sd_height;
+	unsigned int enable_dma_ports, idx;
+	int ret;
+
+	p1_dev->isp_devs[isp_ctx->isp_hw_module].current_frame = 0;
+	p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count = 0;
+
+	isp_ctx->frame_seq_no = 1;
+	atomic_set(&isp_ctx->composed_frame_id, 0);
+
+	/* Get the enabled DMA ports */
+	enable_dma_ports = isp_ctx->enable_dma_ports;
+	dev_dbg(dev, "%s enable_dma_ports:0x%x", __func__, enable_dma_ports);
+
+	/* sensor config */
+	sd_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(cam_dev->sensor,
+			       pad, get_fmt, NULL, &sd_format);
+
+	if (ret) {
+		dev_dbg(dev, "sensor:%s g_fmt on failed:%d\n",
+			cam_dev->sensor->entity.name, ret);
+		return -EPERM;
+	}
+
+	dev_dbg(dev,
+		"sensor get_fmt ret=%d, w=%d, h=%d, code=0x%x, field=%d, color=%d\n",
+		ret, sd_format.format.width, sd_format.format.height,
+		sd_format.format.code, sd_format.format.field,
+		sd_format.format.colorspace);
+
+	config_param.cfg_in_param.continuous = 0x1;
+	config_param.cfg_in_param.subsample = 0x0;
+	/* fix to one pixel mode in default */
+	config_param.cfg_in_param.pixel_mode = one_pixel_mode;
+	/* support normal pattern in default */
+	config_param.cfg_in_param.data_pattern = 0x0;
+
+	config_param.cfg_in_param.crop.left = 0x0;
+	config_param.cfg_in_param.crop.top = 0x0;
+
+	config_param.cfg_in_param.raw_pixel_id =
+		get_sensor_pixel_id(sd_format.format.code);
+	config_param.cfg_in_param.img_fmt =
+		get_sensor_fmt(sd_format.format.code);
+	config_param.cfg_in_param.crop.width = sd_format.format.width;
+	config_param.cfg_in_param.crop.height = sd_format.format.height;
+	sd_width = sd_format.format.width;
+	sd_height = sd_format.format.height;
+
+	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	if ((enable_dma_ports & R_IMGO) == R_IMGO) {
+		struct v4l2_format *imgo_fmt =
+			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+
+		config_param.cfg_main_param.pure_raw = isp_ctx->isp_raw_path;
+		config_param.cfg_main_param.pure_raw_pack = 1;
+		config_param.cfg_main_param.bypass = 0;
+
+		config_param.cfg_main_param.output.img_fmt =
+			get_img_fmt(imgo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_main_param.output.pixel_byte =
+			get_pixel_byte(imgo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_main_param.output.size.w =
+			imgo_fmt->fmt.pix_mp.width;
+		config_param.cfg_main_param.output.size.h =
+			imgo_fmt->fmt.pix_mp.height;
+
+		config_param.cfg_main_param.output.size.stride =
+			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+		config_param.cfg_main_param.output.size.xsize =
+			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+		config_param.cfg_main_param.output.crop.left = 0x0;
+		config_param.cfg_main_param.output.crop.top = 0x0;
+
+		config_param.cfg_main_param.output.crop.width = sd_width;
+		config_param.cfg_main_param.output.crop.height = sd_height;
+
+		WARN_ONCE(imgo_fmt->fmt.pix_mp.width > sd_width ||
+			  imgo_fmt->fmt.pix_mp.height > sd_height,
+			  "img out:%d:%d in:%d:%d",
+			  imgo_fmt->fmt.pix_mp.width,
+			  imgo_fmt->fmt.pix_mp.height,
+			  sd_width,
+			  sd_height);
+
+		dev_dbg(dev,
+			"imgo pixel_byte:%d img_fmt:0x%x raw:%d\n",
+			config_param.cfg_main_param.output.pixel_byte,
+			config_param.cfg_main_param.output.img_fmt,
+			config_param.cfg_main_param.pure_raw);
+		dev_dbg(dev,
+			"imgo param:size=%0dx%0d, stride:%d,xsize:%d,crop=%0dx%0d\n",
+			config_param.cfg_main_param.output.size.w,
+			config_param.cfg_main_param.output.size.h,
+			config_param.cfg_main_param.output.size.stride,
+			config_param.cfg_main_param.output.size.xsize,
+			config_param.cfg_main_param.output.crop.width,
+			config_param.cfg_main_param.output.crop.height);
+	} else {
+		config_param.cfg_main_param.bypass = 1;
+	}
+
+	idx = MTK_CAM_P1_PACKED_BIN_OUT;
+	if ((enable_dma_ports & R_RRZO) == R_RRZO) {
+		struct v4l2_format *rrzo_fmt =
+			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
+
+		config_param.cfg_resize_param.bypass = 0;
+		config_param.cfg_resize_param.output.img_fmt =
+			get_img_fmt(rrzo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_resize_param.output.pixel_byte =
+			get_pixel_byte(rrzo_fmt->fmt.pix_mp.pixelformat);
+		config_param.cfg_resize_param.output.size.w =
+			rrzo_fmt->fmt.pix_mp.width;
+		config_param.cfg_resize_param.output.size.h =
+			rrzo_fmt->fmt.pix_mp.height;
+		config_param.cfg_resize_param.output.size.stride =
+			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+		config_param.cfg_resize_param.output.size.xsize =
+			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+		config_param.cfg_resize_param.output.crop.left = 0x0;
+		config_param.cfg_resize_param.output.crop.top = 0x0;
+		config_param.cfg_resize_param.output.crop.width = sd_width;
+		config_param.cfg_resize_param.output.crop.height = sd_height;
+
+		WARN_ONCE(rrzo_fmt->fmt.pix_mp.width > sd_width ||
+			  rrzo_fmt->fmt.pix_mp.height > sd_height,
+			  "rrz out:%d:%d in:%d:%d",
+			  rrzo_fmt->fmt.pix_mp.width,
+			  rrzo_fmt->fmt.pix_mp.height,
+			  sd_width,
+			  sd_height);
+
+		dev_dbg(dev, "rrzo pixel_byte:%d img_fmt:0x%x\n",
+			config_param.cfg_resize_param.output.pixel_byte,
+			config_param.cfg_resize_param.output.img_fmt);
+		dev_dbg(dev,
+			"rrzo param:size=%0dx%0d,stride:%d,xsize:%d,crop=%0dx%0d\n",
+			config_param.cfg_resize_param.output.size.w,
+			config_param.cfg_resize_param.output.size.h,
+			config_param.cfg_resize_param.output.size.stride,
+			config_param.cfg_resize_param.output.size.xsize,
+			config_param.cfg_resize_param.output.crop.width,
+			config_param.cfg_resize_param.output.crop.height);
+	} else {
+		config_param.cfg_resize_param.bypass = 1;
+	}
+
+	/* Configure meta DMAs info. */
+	config_param.cfg_meta_param.enabled_meta_dmas = enable_dma_ports;
+
+	isp_composer_hw_config(isp_ctx, &config_param);
+
+	dev_dbg(dev, "%s done\n", __func__);
+	return 0;
+}
+
+int mtk_isp_enqueue(struct device *dev,
+		    unsigned int dma_port,
+		    struct mtk_cam_dev_buffer *buffer)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct mtk_isp_scp_p1_cmd frameparams;
+
+	memset(&frameparams, 0, sizeof(frameparams));
+
+	frameparams.cmd_id = ISP_CMD_ENQUEUE_META;
+	frameparams.meta_frame.enabled_dma = dma_port;
+	frameparams.meta_frame.vb_index = buffer->vbb.vb2_buf.index;
+	frameparams.meta_frame.meta_addr.iova = buffer->daddr;
+	frameparams.meta_frame.meta_addr.scp_addr = buffer->scp_addr;
+
+	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_CMD);
+
+	return 0;
+}
+
+int mtk_isp_req_enqueue(struct device *dev,
+			struct mtk_cam_dev_start_param *frameparamsbase)
+{
+	struct isp_p1_device *p1_dev = get_p1_device(dev);
+	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
+	struct p1_frame_param frameparams;
+	struct mtk_isp_queue_job *framejob;
+	struct mtk_cam_dev_buffer **bundle_buffers;
+	unsigned int i, idx;
+
+	framejob = kzalloc(sizeof(*framejob), GFP_ATOMIC);
+	memset(framejob, 0, sizeof(*framejob));
+	memset(&frameparams, 0, sizeof(frameparams));
+	INIT_LIST_HEAD(&framejob->list_buf);
+
+	bundle_buffers = &frameparamsbase->buffers[0];
+	frameparams.frame_seq_no = isp_ctx->frame_seq_no++;
+	frameparams.sof_idx =
+		p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count;
+	framejob->request_fd = frameparamsbase->request_fd;
+	framejob->frame_seq_no = frameparams.frame_seq_no;
+
+	idx = MTK_CAM_P1_META_IN_0;
+	if (bundle_buffers[idx]) {
+		frameparams.tuning_addr.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.tuning_addr.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Image output */
+	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
+	if (bundle_buffers[idx]) {
+		frameparams.img_dma_buffers[0].buffer.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.img_dma_buffers[0].buffer.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		dev_dbg(dev, "main stream address iova:0x%x\n",
+			frameparams.img_dma_buffers[0].buffer.iova);
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Resize output */
+	idx = MTK_CAM_P1_PACKED_BIN_OUT;
+	if (bundle_buffers[idx]) {
+		frameparams.img_dma_buffers[1].buffer.iova =
+			bundle_buffers[idx]->daddr;
+		frameparams.img_dma_buffers[1].buffer.scp_addr =
+			bundle_buffers[idx]->scp_addr;
+		dev_dbg(dev, "packed out address iova:0x%x\n",
+			frameparams.img_dma_buffers[1].buffer.iova);
+		list_add_tail(&bundle_buffers[idx]->list,
+			      &framejob->list_buf);
+	}
+
+	/* Meta output DMAs */
+	for (i = 0; i < MAX_META_DMA_NODES; i++) {
+		idx = MTK_CAM_P1_META_OUT_0 + i;
+		if (bundle_buffers[idx]) {
+			frameparams.meta_addrs[i].iova =
+			  bundle_buffers[idx]->daddr;
+			frameparams.meta_addrs[i].scp_addr =
+			  bundle_buffers[idx]->scp_addr;
+			list_add_tail(&bundle_buffers[idx]->list,
+				      &framejob->list_buf);
+		} else {
+			frameparams.meta_addrs[i].iova = 0;
+			frameparams.meta_addrs[i].scp_addr = 0;
+		}
+	}
+
+	spin_lock(&isp_ctx->p1_enqueue_list.lock);
+	list_add_tail(&framejob->list_entry, &isp_ctx->p1_enqueue_list.queue);
+	atomic_inc(&isp_ctx->p1_enqueue_list.queue_cnt);
+	spin_unlock(&isp_ctx->p1_enqueue_list.lock);
+
+	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_FRAME);
+	dev_dbg(dev, "request fd:%d frame_seq_no:%d is queued cnt:%d\n",
+		frameparamsbase->request_fd,
+		frameparams.frame_seq_no,
+		atomic_read(&isp_ctx->p1_enqueue_list.queue_cnt));
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_isp_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_suspend, mtk_isp_resume)
+	SET_RUNTIME_PM_OPS(mtk_isp_suspend, mtk_isp_resume, NULL)
+};
+
+static struct platform_driver mtk_isp_driver = {
+	.probe   = mtk_isp_probe,
+	.remove  = mtk_isp_remove,
+	.driver  = {
+		.name  = "mtk-cam",
+		.of_match_table = of_match_ptr(mtk_isp_of_ids),
+		.pm     = &mtk_isp_pm_ops,
+	}
+};
+
+module_platform_driver(mtk_isp_driver);
+
+MODULE_DESCRIPTION("Camera ISP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
new file mode 100644
index 000000000000..6cf8bb4ba93a
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryan Yu <ryan.yu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CAMERA_ISP_H
+#define __CAMERA_ISP_H
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/irqreturn.h>
+#include <linux/miscdevice.h>
+#include <linux/pm_qos.h>
+#include <linux/scatterlist.h>
+
+#include "mtk_cam-dev.h"
+#include "mtk_cam-scp.h"
+
+#define CAM_A_MAX_WIDTH		3328U
+#define CAM_A_MAX_HEIGHT	2496U
+#define CAM_B_MAX_WIDTH		5376U
+#define CAM_B_MAX_HEIGHT	4032U
+
+#define CAM_MIN_WIDTH		80U
+#define CAM_MIN_HEIGHT		60U
+
+#define IMG_MAX_WIDTH		CAM_B_MAX_WIDTH
+#define IMG_MAX_HEIGHT		CAM_B_MAX_HEIGHT
+#define IMG_MIN_WIDTH		CAM_MIN_WIDTH
+#define IMG_MIN_HEIGHT		CAM_MIN_HEIGHT
+
+#define RRZ_MAX_WIDTH		CAM_B_MAX_WIDTH
+#define RRZ_MAX_HEIGHT		CAM_B_MAX_HEIGHT
+#define RRZ_MIN_WIDTH		CAM_MIN_WIDTH
+#define RRZ_MIN_HEIGHT		CAM_MIN_HEIGHT
+
+#define R_IMGO		BIT(0)
+#define R_RRZO		BIT(1)
+#define R_AAO		BIT(3)
+#define R_AFO		BIT(4)
+#define R_LCSO		BIT(5)
+#define R_PDO		BIT(6)
+#define R_LMVO		BIT(7)
+#define R_FLKO		BIT(8)
+#define R_RSSO		BIT(9)
+#define R_PSO		BIT(10)
+
+#define ISP_COMPOSING_MAX_NUM		4
+#define ISP_FRAME_COMPOSING_MAX_NUM	3
+
+#define IRQ_DATA_BUF_SIZE		4
+#define COMPOSRE_EVENT_BUF_SIZE		4
+
+#define CQ_ADDRESS_OFFSET		0x640
+#define CQ_BUFFER_COUNT			3
+
+#define IRQ_STAT_STR "cam%c, SOF_%d irq(0x%x), " \
+			"dma(0x%x), frame_num(%d)/cq_num(%d)\n"
+
+/*
+ * In order with the sequence of device nodes defined in dtsi rule,
+ * one hardware module should be mapping to one node.
+ */
+enum isp_dev_node_enum {
+	ISP_CAMSYS_CONFIG_IDX = 0,
+	ISP_CAM_UNI_IDX,
+	ISP_CAM_A_IDX,
+	ISP_CAM_B_IDX,
+	ISP_DEV_NODE_NUM
+};
+
+/* Image RAW path for ISP P1 module. */
+enum isp_raw_path_enum {
+	ISP_PROCESS_RAW_PATH = 0,
+	ISP_PURE_RAW_PATH
+};
+
+enum {
+	img_fmt_unknown		= 0x0000,
+	img_fmt_raw_start	= 0x2200,
+	img_fmt_bayer8		= img_fmt_raw_start,
+	img_fmt_bayer10,
+	img_fmt_bayer12,
+	img_fmt_bayer14,
+	img_fmt_fg_bayer8,
+	img_fmt_fg_bayer10,
+	img_fmt_fg_bayer12,
+	img_fmt_fg_bayer14,
+};
+
+enum {
+	raw_pxl_id_b   = 0,
+	raw_pxl_id_gb,
+	raw_pxl_id_gr,
+	raw_pxl_id_r
+};
+
+enum {
+	default_pixel_mode = 0,
+	one_pixel_mode,
+	two_pixel_mode,
+	four_pixel_mode,
+	pixel_mode_num,
+};
+
+enum mtk_isp_scp_ipi_type {
+	SCP_ISP_CMD = 0,
+	SCP_ISP_FRAME,
+};
+
+struct isp_queue {
+	struct list_head queue;
+	atomic_t queue_cnt;
+	spinlock_t lock; /* queue attributes protection */
+};
+
+struct isp_thread {
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+};
+
+struct mtk_isp_queue_work {
+	union {
+		struct mtk_isp_scp_p1_cmd cmd;
+		struct p1_frame_param frameparams;
+	};
+	struct list_head list_entry;
+	enum mtk_isp_scp_ipi_type type;
+};
+
+struct mtk_cam_dev_stat_event_data {
+	__u32 frame_seq_no;
+	__u32 meta0_vb2_index;
+	__u32 meta1_vb2_index;
+	__u32 irq_status_mask;
+	__u32 dma_status_mask;
+};
+
+struct mtk_isp_queue_job {
+	struct list_head list_entry;
+	struct list_head list_buf;
+	unsigned int request_fd;
+	unsigned int frame_seq_no;
+};
+
+struct isp_clk_struct {
+	int num_clks;
+	struct clk_bulk_data *clk_list;
+};
+
+struct isp_device {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	spinlock_t spinlock_irq; /* ISP reg setting integrity */
+	unsigned int current_frame;
+	unsigned int meta0_vb2_index;
+	unsigned int meta1_vb2_index;
+	u8 sof_count;
+	u8 isp_hw_module;
+};
+
+struct mtk_isp_p1_ctx {
+	struct isp_queue composer_txlist;
+	struct isp_thread composer_tx_thread;
+	atomic_t cmd_queued;
+	struct mutex composer_tx_lock; /* isp composer work protection */
+
+	struct isp_thread composer_rx_thread;
+	struct mtk_isp_scp_p1_cmd composer_evts[COMPOSRE_EVENT_BUF_SIZE];
+	atomic_t composer_evts_start;
+	atomic_t composer_evts_end;
+	spinlock_t composer_evts_lock; /* SCP events protection */
+	/* increase after ipi */
+	atomic_t ipi_occupied;
+	/* increase after frame enqueue */
+	atomic_t composing_frame;
+	/* current composed frame id */
+	atomic_t composed_frame_id;
+
+	struct isp_queue p1_enqueue_list;
+
+	struct isp_thread isp_deque_thread;
+	struct mtk_cam_dev_stat_event_data irq_event_datas[IRQ_DATA_BUF_SIZE];
+	atomic_t irq_data_start;
+	atomic_t irq_data_end;
+	spinlock_t irq_dequeue_lock; /* ISP frame dequeuq protection */
+
+	dma_addr_t scp_mem_pa;
+	dma_addr_t scp_mem_iova;
+	struct sg_table sgtable;
+
+	/* increase after open, decrease when close */
+	atomic_t isp_user_cnt;
+	/* frame sequence number, increase per en-queue*/
+	int frame_seq_no;
+	unsigned int isp_hw_module;
+	unsigned int isp_raw_path;
+	unsigned int enable_dma_ports;
+
+	void (*composer_deinit_donecb)(void *isp_ctx);
+
+	struct list_head list;
+};
+
+struct isp_p1_device {
+	struct platform_device *pdev;
+
+	/* for SCP driver  */
+	struct platform_device *scp_pdev;
+	struct rproc *rproc_handle;
+
+	struct mtk_isp_p1_ctx isp_ctx;
+	struct isp_clk_struct isp_clk;
+	struct mtk_cam_dev *cam_dev;
+	struct isp_device *isp_devs;
+};
+
+static inline struct isp_p1_device *
+p1_ctx_to_dev(const struct mtk_isp_p1_ctx *__p1_ctx)
+{
+	return container_of(__p1_ctx, struct isp_p1_device, isp_ctx);
+}
+
+static inline struct isp_p1_device *get_p1_device(struct device *dev)
+{
+	return ((struct isp_p1_device *)dev_get_drvdata(dev));
+}
+
+int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx);
+int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx);
+void isp_composer_meta_config(struct mtk_isp_p1_ctx *isp_ctx,
+			      unsigned int dma);
+void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
+			    struct p1_config_param *config_param);
+void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on);
+void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
+			    void (*donecb)(void *data));
+void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
+			  void *data,
+			  enum mtk_isp_scp_ipi_type type);
+
+/**
+ * mtk_isp_open - open isp driver and initialize related resources.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_open(struct device *dev);
+
+/**
+ * mtk_isp_release - release isp driver and related resources.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_release(struct device *dev);
+
+/**
+ * mtk_isp_config - output image & meta data configuration.
+ *
+ * @dev:	isp device.
+ *
+ */
+int mtk_isp_config(struct device *dev);
+
+/**
+ * mtk_isp_req_enqueue - enqueue a frame bundle (per-frame basis) to ISP driver.
+ *
+ * @dev:	isp device.
+ * @frameparamsbase: pointer to &struct mtk_cam_dev_start_param.
+ *
+ */
+int mtk_isp_req_enqueue(struct device *dev,
+			struct mtk_cam_dev_start_param *frameparamsbase);
+
+/**
+ * mtk_isp_enqueue - enqueue a single frame to ISP driver
+ * for non-per-frame DMA.
+ *
+ * @dev:	isp device.
+ * @buffer: pointer to &struct mtk_cam_dev_buffer.
+ *
+ */
+int mtk_isp_enqueue(struct device *dev,
+		    unsigned int dma_idx,
+		    struct mtk_cam_dev_buffer *buffer);
+#endif /*__CAMERA_ISP_H*/
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 10/11] media: platform: Add Mediatek ISP P1 SCP communication
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (11 preceding siblings ...)
  2019-05-10  1:58 ` [RFC,V2,09/11] media: platform: Add Mediatek ISP P1 device driver Jungo Lin
@ 2019-05-10  1:58 ` Jungo Lin
  2019-05-10  1:58 ` [RFC, V2, 11/11] media: platform: Add Mediatek ISP P1 shared memory device Jungo Lin
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:58 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds communication with the co-processor on the SoC
through the SCP driver. It supports bi-directional commands
to exchange data and perform command flow control function.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
This patch dependents on "Add support for mt8183 SCP"[1].

[1] https://patchwork.kernel.org/cover/10872547/
---
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c | 481 ++++++++++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h | 207 ++++++++
 2 files changed, 688 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
new file mode 100644
index 000000000000..cd10a1c43e0b
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Seraph Huang <seraph.huang@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/kthread.h>
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/remoteproc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include "mtk_cam.h"
+
+static int isp_composer_dma_sg_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	u32 size, size_align;
+	struct sg_table *sgt;
+	struct page **pages;
+	unsigned int n_pages, i;
+	int ret;
+
+	isp_ctx->scp_mem_pa = scp_get_reserve_mem_phys(SCP_ISP_MEM_ID);
+	size = scp_get_reserve_mem_size(SCP_ISP_MEM_ID);
+
+	dev_dbg(dev, "scp mem addr:%pad size:%u MiB\n", &isp_ctx->scp_mem_pa,
+		(size / SZ_1M));
+
+	/* get iova address */
+	sgt = &isp_ctx->sgtable;
+	sg_alloc_table(sgt, 1, GFP_KERNEL);
+
+	size_align = PAGE_ALIGN(size);
+	n_pages = size_align >> PAGE_SHIFT;
+
+	pages = kmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
+	if (!pages)
+		goto fail_pages_alloc;
+
+	for (i = 0; i < n_pages; i++)
+		pages[i] = phys_to_page(isp_ctx->scp_mem_pa + i * PAGE_SIZE);
+
+	ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+					0, size_align, GFP_KERNEL);
+	if (ret) {
+		dev_err(dev, "failed to allocate sg table:%d\n", ret);
+		goto fail_table_alloc;
+	}
+	sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents,
+				      DMA_BIDIRECTIONAL,
+				      DMA_ATTR_SKIP_CPU_SYNC);
+	if (!sgt->nents) {
+		dev_err(dev, "failed to dma sg map\n");
+		goto fail_map;
+	}
+
+	isp_ctx->scp_mem_iova = sg_dma_address(sgt->sgl);
+
+	return 0;
+
+fail_map:
+	sg_free_table(sgt);
+fail_table_alloc:
+	while (n_pages--)
+		__free_page(pages[n_pages]);
+	kfree(pages);
+fail_pages_alloc:
+	return -ENOMEM;
+}
+
+static void isp_composer_deinit(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct mtk_isp_queue_work *ipi_job, *tmp_ipi_job;
+
+	list_for_each_entry_safe(ipi_job, tmp_ipi_job,
+				 &isp_ctx->composer_txlist.queue,
+				 list_entry) {
+		list_del(&ipi_job->list_entry);
+		kfree(ipi_job);
+		atomic_dec(&isp_ctx->composer_txlist.queue_cnt);
+	}
+
+	atomic_set(&isp_ctx->ipi_occupied, 0);
+	atomic_set(&isp_ctx->composing_frame, 0);
+	mutex_destroy(&isp_ctx->composer_tx_lock);
+
+	dma_unmap_sg_attrs(&p1_dev->pdev->dev, isp_ctx->sgtable.sgl,
+			   isp_ctx->sgtable.orig_nents,
+			   DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
+	sg_free_table(&isp_ctx->sgtable);
+
+	if (!IS_ERR(isp_ctx->composer_tx_thread.thread)) {
+		kthread_stop(isp_ctx->composer_tx_thread.thread);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		isp_ctx->composer_tx_thread.thread = NULL;
+	}
+
+	isp_ctx->composer_deinit_donecb(isp_ctx);
+}
+
+/*
+ * Two kinds of flow control in isp_composer_tx_work.
+ *
+ * Case 1: IPI commands flow control. The maximum number of command queues is 3.
+ * There are two types of IPI commands (SCP_ISP_CMD/SCP_ISP_FRAME) in P1 driver.
+ * It is controlled by ipi_occupied.
+ * The priority of SCP_ISP_CMD is higher than SCP_ISP_FRAME.
+ *
+ * Case 2: Frame buffers flow control. The maximum number of frame buffers is 3.
+ * It is controlled by composing_frame.
+ * Frame buffer is sent by SCP_ISP_FRAME command.
+ */
+static int isp_composer_tx_work(void *data)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_isp_queue_work *isp_composer_work, *tmp_ipi_job;
+	struct isp_queue *composer_txlist = &isp_ctx->composer_txlist;
+	int ret;
+
+	while (1) {
+		ret = wait_event_interruptible
+			(isp_ctx->composer_tx_thread.wq,
+			 (atomic_read(&composer_txlist->queue_cnt) > 0 &&
+			 atomic_read(&isp_ctx->ipi_occupied)
+				< ISP_COMPOSING_MAX_NUM &&
+			 atomic_read(&isp_ctx->composing_frame)
+				< ISP_FRAME_COMPOSING_MAX_NUM) ||
+			 atomic_read(&isp_ctx->cmd_queued) > 0 &&
+			 atomic_read(&isp_ctx->ipi_occupied)
+				< ISP_COMPOSING_MAX_NUM ||
+			 kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock(&composer_txlist->lock);
+		if (atomic_read(&isp_ctx->cmd_queued) > 0) {
+			list_for_each_entry_safe(isp_composer_work, tmp_ipi_job,
+						 &composer_txlist->queue,
+						 list_entry) {
+				if (isp_composer_work->type == SCP_ISP_CMD) {
+					dev_dbg(dev, "Found a cmd\n");
+					break;
+				}
+			}
+		} else {
+			if (atomic_read(&isp_ctx->composing_frame) >=
+				ISP_FRAME_COMPOSING_MAX_NUM) {
+				spin_unlock(&composer_txlist->lock);
+				continue;
+			}
+			isp_composer_work =
+			    list_first_entry_or_null
+				(&composer_txlist->queue,
+				 struct mtk_isp_queue_work,
+				 list_entry);
+		}
+
+		list_del(&isp_composer_work->list_entry);
+		atomic_dec(&composer_txlist->queue_cnt);
+		spin_unlock(&composer_txlist->lock);
+
+		if (isp_composer_work->type == SCP_ISP_CMD) {
+			mutex_lock(&isp_ctx->composer_tx_lock);
+			scp_ipi_send
+				(p1_dev->scp_pdev,
+				 SCP_IPI_ISP_CMD,
+				 &isp_composer_work->cmd,
+				 sizeof(isp_composer_work->cmd),
+				 0);
+			mutex_unlock(&isp_ctx->composer_tx_lock);
+			atomic_dec(&isp_ctx->cmd_queued);
+			atomic_inc(&isp_ctx->ipi_occupied);
+			dev_dbg(dev,
+				"%s cmd id %d sent, %d ipi buf occupied",
+				__func__,
+				isp_composer_work->cmd.cmd_id,
+				atomic_read(&isp_ctx->ipi_occupied));
+		} else if (isp_composer_work->type == SCP_ISP_FRAME) {
+			mutex_lock(&isp_ctx->composer_tx_lock);
+			scp_ipi_send
+				(p1_dev->scp_pdev,
+				 SCP_IPI_ISP_FRAME,
+				 &isp_composer_work->frameparams,
+				 sizeof(isp_composer_work->frameparams),
+				 0);
+			mutex_unlock(&isp_ctx->composer_tx_lock);
+			atomic_inc(&isp_ctx->ipi_occupied);
+			atomic_inc(&isp_ctx->composing_frame);
+			dev_dbg(dev,
+				"%s frame %d sent, %d ipi, %d CQ bufs occupied",
+				__func__,
+				isp_composer_work->frameparams.frame_seq_no,
+				atomic_read(&isp_ctx->ipi_occupied),
+				atomic_read(&isp_ctx->composing_frame));
+		} else {
+			dev_err(dev,
+				"ignore IPI type: %d!\n",
+				isp_composer_work->type);
+			kfree(isp_composer_work);
+			continue;
+		}
+		kfree(isp_composer_work);
+	}
+	return ret;
+}
+
+static int isp_composer_rx_work(void *data)
+{
+	struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_isp_scp_p1_cmd ipi_msg;
+	int ret;
+	unsigned int ipi_queue_occupied, i;
+	unsigned long flags;
+	atomic_t *evts_end = &isp_ctx->composer_evts_end;
+	atomic_t *evts_start = &isp_ctx->composer_evts_start;
+	u8 ack_cmd_id;
+
+	while (1) {
+		ret = wait_event_interruptible(isp_ctx->composer_rx_thread.wq,
+					       (atomic_read(evts_end) !=
+					       atomic_read(evts_start)) ||
+					       kthread_should_stop());
+
+		if (kthread_should_stop())
+			break;
+
+		if (ret == ERESTARTSYS) {
+			dev_err(dev, "interrupted by a signal!\n");
+			continue;
+		}
+
+		spin_lock_irqsave(&isp_ctx->composer_evts_lock, flags);
+		i = atomic_read(evts_start);
+		memcpy(&ipi_msg, &isp_ctx->composer_evts[i],
+		       sizeof(struct mtk_isp_scp_p1_cmd));
+		atomic_set(evts_start, ++i & 0x3);
+		spin_unlock_irqrestore(&isp_ctx->composer_evts_lock, flags);
+
+		switch (ipi_msg.cmd_id) {
+		case ISP_CMD_ACK:
+			ipi_queue_occupied =
+				atomic_dec_return(&isp_ctx->ipi_occupied);
+			if (ipi_queue_occupied < ISP_COMPOSING_MAX_NUM)
+				wake_up_interruptible
+					(&isp_ctx->composer_tx_thread.wq);
+
+			ack_cmd_id = ipi_msg.ack_info.cmd_id;
+			if (ack_cmd_id == ISP_CMD_FRAME_ACK) {
+				dev_dbg(dev,
+					"%s frame %d ack\n",
+					__func__,
+					ipi_msg.ack_info.frame_seq_no);
+				atomic_set(&isp_ctx->composed_frame_id,
+					   ipi_msg.ack_info.frame_seq_no);
+			} else {
+				dev_dbg(dev, "%s cmd id: %d",
+					__func__,
+					ack_cmd_id);
+				if (ack_cmd_id == ISP_CMD_DEINIT) {
+					isp_composer_deinit(isp_ctx);
+					isp_ctx->composer_rx_thread.thread =
+						NULL;
+					return -1;
+				}
+			}
+			break;
+		default:
+			break;
+		};
+	}
+	return ret;
+}
+
+static void isp_composer_handler(void *data, unsigned int len, void *priv)
+{
+	struct mtk_isp_p1_ctx *isp_ctx;
+	struct mtk_isp_scp_p1_cmd *ipi_msg_ptr;
+	unsigned long flags;
+	unsigned int i;
+
+	ipi_msg_ptr = (struct mtk_isp_scp_p1_cmd *)data;
+	isp_ctx = (struct mtk_isp_p1_ctx *)priv;
+
+	spin_lock_irqsave(&isp_ctx->composer_evts_lock, flags);
+	i = atomic_read(&isp_ctx->composer_evts_end);
+	memcpy(&isp_ctx->composer_evts[i], data,
+	       sizeof(struct mtk_isp_scp_p1_cmd));
+	atomic_set(&isp_ctx->composer_evts_end, ++i & 0x3);
+	spin_unlock_irqrestore(&isp_ctx->composer_evts_lock, flags);
+
+	wake_up_interruptible(&isp_ctx->composer_rx_thread.wq);
+}
+
+int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+	int ret;
+
+	ret = scp_ipi_register(p1_dev->scp_pdev,
+			       SCP_IPI_ISP_CMD,
+			       isp_composer_handler,
+			       isp_ctx);
+	if (ret)
+		return ret;
+
+	if (!isp_ctx->composer_tx_thread.thread) {
+		mutex_init(&isp_ctx->composer_tx_lock);
+		init_waitqueue_head(&isp_ctx->composer_tx_thread.wq);
+		INIT_LIST_HEAD(&isp_ctx->composer_txlist.queue);
+		spin_lock_init(&isp_ctx->composer_txlist.lock);
+		isp_ctx->composer_tx_thread.thread =
+			kthread_run(isp_composer_tx_work, (void *)isp_ctx,
+				    "isp_composer_tx");
+		if (IS_ERR(isp_ctx->composer_tx_thread.thread)) {
+			dev_err(dev, "unable to start kthread\n");
+			isp_ctx->composer_tx_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+	atomic_set(&isp_ctx->composer_txlist.queue_cnt, 0);
+
+	if (!isp_ctx->composer_rx_thread.thread) {
+		init_waitqueue_head(&isp_ctx->composer_rx_thread.wq);
+		isp_ctx->composer_rx_thread.thread =
+			kthread_run(isp_composer_rx_work, (void *)isp_ctx,
+				    "isp_composer_rx");
+		if (IS_ERR(isp_ctx->composer_rx_thread.thread)) {
+			dev_err(dev, "unable to start kthread\n");
+			isp_ctx->composer_rx_thread.thread = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	atomic_set(&isp_ctx->composer_evts_start, 0);
+	atomic_set(&isp_ctx->composer_evts_end, 0);
+	spin_lock_init(&isp_ctx->composer_evts_lock);
+
+	atomic_set(&isp_ctx->ipi_occupied, 0);
+	atomic_set(&isp_ctx->composing_frame, 0);
+	atomic_set(&isp_ctx->cmd_queued, 0);
+
+	return 0;
+}
+
+void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
+			  void *data,
+			  enum mtk_isp_scp_ipi_type type)
+{
+	struct mtk_isp_queue_work *isp_composer_work;
+	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
+	struct device *dev = &p1_dev->pdev->dev;
+
+	isp_composer_work = kzalloc(sizeof(*isp_composer_work), GFP_KERNEL);
+	isp_composer_work->type = type;
+	switch (type) {
+	case SCP_ISP_CMD:
+		memcpy(&isp_composer_work->cmd, data,
+		       sizeof(isp_composer_work->cmd));
+
+		spin_lock(&isp_ctx->composer_txlist.lock);
+		list_add_tail(&isp_composer_work->list_entry,
+			      &isp_ctx->composer_txlist.queue);
+		atomic_inc(&isp_ctx->composer_txlist.queue_cnt);
+		spin_unlock(&isp_ctx->composer_txlist.lock);
+
+		dev_dbg(dev, "Enq ipi cmd id:%d\n",
+			isp_composer_work->cmd.cmd_id);
+		atomic_inc(&isp_ctx->cmd_queued);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		break;
+	case SCP_ISP_FRAME:
+		memcpy(&isp_composer_work->frameparams, data,
+		       sizeof(isp_composer_work->frameparams));
+
+		spin_lock(&isp_ctx->composer_txlist.lock);
+		list_add_tail(&isp_composer_work->list_entry,
+			      &isp_ctx->composer_txlist.queue);
+		atomic_inc(&isp_ctx->composer_txlist.queue_cnt);
+		spin_unlock(&isp_ctx->composer_txlist.lock);
+
+		dev_dbg(dev, "Enq ipi frame_num:%d\n",
+			isp_composer_work->frameparams.frame_seq_no);
+		wake_up_interruptible(&isp_ctx->composer_tx_thread.wq);
+		break;
+	default:
+		break;
+	}
+}
+
+int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx)
+{
+	struct img_buffer frameparam;
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+	int ret;
+
+	ret = isp_composer_dma_sg_init(isp_ctx);
+	if (ret)
+		return ret;
+
+	frameparam.scp_addr = isp_ctx->scp_mem_pa;
+	frameparam.iova = isp_ctx->scp_mem_iova;
+
+	composer_tx_cmd.cmd_id = ISP_CMD_INIT;
+	composer_tx_cmd.frameparam.hw_module = isp_ctx->isp_hw_module;
+	memcpy(&composer_tx_cmd.frameparam.cq_addr, &frameparam,
+	       sizeof(struct img_buffer));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+
+	return 0;
+}
+
+void isp_composer_meta_config(struct mtk_isp_p1_ctx *isp_ctx,
+			      unsigned int dma)
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_CONFIG_META;
+	memcpy(&composer_tx_cmd.cfg_meta_out_param, &dma, sizeof(dma));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
+
+void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
+			    struct p1_config_param *config_param)
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_CONFIG;
+	memcpy(&composer_tx_cmd.frameparam,
+	       config_param,
+	       sizeof(*config_param));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
+
+void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on)
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_STREAM;
+	memcpy(&composer_tx_cmd.is_stream_on, &on, sizeof(on));
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
+
+void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
+			    void (*donecb)(void *data))
+{
+	struct mtk_isp_scp_p1_cmd composer_tx_cmd;
+
+	memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
+	composer_tx_cmd.cmd_id = ISP_CMD_DEINIT;
+	isp_ctx->composer_deinit_donecb = donecb;
+	isp_composer_enqueue(isp_ctx, &composer_tx_cmd, SCP_ISP_CMD);
+}
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
new file mode 100644
index 000000000000..54025dfdb4e2
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Seraph Huang <seraph.huang@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_ISP_SCP_H
+#define _MTK_ISP_SCP_H
+
+#include <linux/platform_data/mtk_scp.h>
+#include <linux/remoteproc.h>
+#include <linux/types.h>
+
+#define MAX_IMG_DMA_PORT	2
+#define MAX_META_DMA_NODES	4
+
+/*
+ * struct img_size - image size information.
+ *
+ * @w: image width, the unit is pixel
+ * @h: image height, the unit is pixel
+ * @xsize: bytes per line based on width.
+ * @stride: bytes per line when changing line.
+ *          Normally, calculate new STRIDE based on
+ *          xsize + HW constrain(page or align).
+ *
+ */
+struct img_size {
+	__u32 w;
+	__u32 h;
+	__u32 xsize;
+	__u32 stride;
+} __packed;
+
+/*
+ * struct img_buffer - buffer address information.
+ *
+ * @iova: DMA address for external devices.
+ * @scp_addr: SCP address for external co-process unit.
+ *
+ */
+struct img_buffer {
+	__u32 iova;
+	__u32 scp_addr;
+} __packed;
+
+struct p1_img_crop {
+	__u32 left;
+	__u32 top;
+	__u32 width;
+	__u32 height;
+} __packed;
+
+struct p1_img_output {
+	struct img_buffer buffer;
+	struct img_size size;
+	struct p1_img_crop crop;
+	__u8 pixel_byte;
+	__u32 img_fmt;
+} __packed;
+
+/*
+ * struct cfg_in_param - image input parameters structure.
+ *                       Normally, it comes from sensor information.
+ *
+ * @continuous: indicate the sensor mode.
+ *              1: continuous
+ *              0: single
+ * @subsample: indicate to enables SOF subsample or not.
+ * @pixel_mode: describe 1/2/4 pixels per clock cycle.
+ * @data_pattern: describe input data pattern.
+ * @raw_pixel_id: bayer sequence.
+ * @tg_fps: the fps rate of TG (time generator).
+ * @img_fmt: the image format of input source.
+ * @p1_img_crop: the crop configuration of input source.
+ *
+ */
+struct cfg_in_param {
+	__u8 continuous;
+	__u8 subsample;
+	__u8 pixel_mode;
+	__u8 data_pattern;
+	__u8 raw_pixel_id;
+	__u16 tg_fps;
+	__u32 img_fmt;
+	struct p1_img_crop crop;
+} __packed;
+
+/*
+ * struct cfg_main_out_param - the image output parameters of main stream.
+ *
+ * @bypass: indicate this device is enabled or disabled or not .
+ * @pure_raw: indicate the image path control.
+ *            1: pure raw
+ *            0: processing raw
+ * @pure_raw_pack: indicate the image is packed or not.
+ *                 1: packed mode
+ *                 0: unpacked mode
+ * @p1_img_output: the output image information.
+ *
+ */
+struct cfg_main_out_param {
+	/* Bypass main out parameters */
+	__u8 bypass;
+	/* Control HW image raw path */
+	__u8 pure_raw;
+	/* Control HW image pack function */
+	__u8 pure_raw_pack;
+	struct p1_img_output output;
+} __packed;
+
+/*
+ * struct cfg_resize_out_param - the image output parameters of
+ *                               packed out stream.
+ *
+ * @bypass: indicate this device is enabled or disabled or not .
+ * @p1_img_output: the output image information.
+ *
+ */
+struct cfg_resize_out_param {
+	/* Bypass resize parameters */
+	__u8 bypass;
+	struct p1_img_output output;
+} __packed;
+
+/*
+ * struct cfg_meta_out_param - output meta information.
+ *
+ * @enabled_meta_dmas: indicate which meta DMAs are enabled.
+ *
+ */
+struct cfg_meta_out_param {
+	__u32 enabled_meta_dmas;
+} __packed;
+
+struct p1_config_param {
+	/* Sensor/TG info */
+	struct cfg_in_param cfg_in_param;
+	/* IMGO DMA */
+	struct cfg_main_out_param cfg_main_param;
+	/* RRZO DMA */
+	struct cfg_resize_out_param cfg_resize_param;
+	/* 3A DMAs and other. */
+	struct cfg_meta_out_param cfg_meta_param;
+} __packed;
+
+struct p1_frame_param {
+	/* frame sequence number */
+	__u32 frame_seq_no;
+	/* SOF index */
+	__u32 sof_idx;
+	/* The memory address of tuning buffer from user space */
+	struct img_buffer tuning_addr;
+	struct p1_img_output img_dma_buffers[MAX_IMG_DMA_PORT];
+	struct img_buffer meta_addrs[MAX_META_DMA_NODES];
+} __packed;
+
+struct P1_meta_frame {
+	__u32 enabled_dma;
+	__u32 vb_index;
+	struct img_buffer meta_addr;
+} __packed;
+
+struct isp_init_info {
+	__u8 hw_module;
+	struct img_buffer cq_addr;
+} __packed;
+
+struct isp_ack_info {
+	__u8 cmd_id;
+	__u32 frame_seq_no;
+} __packed;
+
+enum mtk_isp_scp_CMD {
+	ISP_CMD_INIT,
+	ISP_CMD_CONFIG,
+	ISP_CMD_STREAM,
+	ISP_CMD_DEINIT,
+	ISP_CMD_ACK,
+	ISP_CMD_FRAME_ACK,
+	ISP_CMD_CONFIG_META,
+	ISP_CMD_ENQUEUE_META,
+	ISP_CMD_RESERVED,
+};
+
+struct mtk_isp_scp_p1_cmd {
+	__u8 cmd_id;
+	union {
+		struct isp_init_info frameparam;
+		struct p1_config_param config_param;
+		struct cfg_meta_out_param cfg_meta_out_param;
+		struct P1_meta_frame meta_frame;
+		__u8 is_stream_on;
+		struct isp_ack_info ack_info;
+	};
+} __packed;
+
+#endif /* _MTK_ISP_SCP_H */
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC, V2, 11/11] media: platform: Add Mediatek ISP P1 shared memory device
       [not found] <Jungo Lin <jungo.lin@mediatek.com>
                   ` (12 preceding siblings ...)
  2019-05-10  1:58 ` [RFC, V2, 10/11] media: platform: Add Mediatek ISP P1 SCP communication Jungo Lin
@ 2019-05-10  1:58 ` Jungo Lin
  2019-08-07 12:47 ` [RFC, v4, 0/4] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  2019-09-02  7:51 ` [RFC, v5, 0/5] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  15 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-10  1:58 UTC (permalink / raw)
  To: tfiga, hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, Jungo Lin, sj.huang,
	yuzhao, linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

The purpose of this child device is to provide shared
memory management for exchanging tuning data between co-processor
and the Pass 1 unit of the camera ISP system, including cache
buffer handling.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../mtk-isp/isp_50/cam/mtk_cam-smem-dev.c     | 297 ++++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-smem.h         |  28 ++
 2 files changed, 325 insertions(+)
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-dev.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h

diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-dev.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-dev.c
new file mode 100644
index 000000000000..b7c8d3a00c2c
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem-dev.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/cacheflush.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+
+#include "mtk_cam-smem.h"
+
+static struct reserved_mem *cam_reserved_smem;
+static struct dma_map_ops smem_dma_ops;
+
+struct mtk_cam_smem_dev {
+	struct device *dev;
+	struct sg_table sgt;
+	struct page **smem_pages;
+	dma_addr_t smem_base;
+	dma_addr_t smem_dma_base;
+	int smem_size;
+};
+
+struct dma_coherent_mem {
+	void		*virt_base;
+	dma_addr_t	device_base;
+	unsigned long	pfn_base;
+	int		size;
+	int		flags;
+	unsigned long	*bitmap;
+	spinlock_t	spinlock; /* dma_coherent_mem attributes protection */
+	bool		use_dev_dma_pfn_offset;
+};
+
+dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *dev,
+					 dma_addr_t iova)
+{
+	struct iommu_domain *domain;
+	dma_addr_t addr, limit;
+	struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev);
+
+	domain = iommu_get_domain_for_dev(dev);
+	if (!domain) {
+		dev_warn(dev, "No iommu group domain\n");
+		return 0;
+	}
+
+	addr = iommu_iova_to_phys(domain, iova);
+	limit = smem_dev->smem_base + smem_dev->smem_size;
+	if (addr < smem_dev->smem_base || addr >= limit) {
+		dev_err(dev,
+			"Unexpected scp_addr:%pad must >= %pad and < %pad)\n",
+			&addr, &smem_dev->smem_base, &limit);
+		return 0;
+	}
+	return addr;
+}
+
+static int mtk_cam_smem_get_sgtable(struct device *dev,
+				    struct sg_table *sgt,
+	void *cpu_addr, dma_addr_t dma_addr,
+	size_t size, unsigned long attrs)
+{
+	struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev);
+	size_t pages_count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+	dma_addr_t scp_addr = mtk_cam_smem_iova_to_scp_addr(dev, dma_addr);
+	u32 pages_start = (scp_addr - smem_dev->smem_base) >> PAGE_SHIFT;
+
+	dev_dbg(dev,
+		"%s:page:%u va:%pK scp addr:%pad, aligned size:%d pages:%u\n",
+		__func__, pages_start, cpu_addr, &scp_addr, size, pages_count);
+
+	return sg_alloc_table_from_pages(sgt,
+		smem_dev->smem_pages + pages_start,
+		pages_count, 0, size, GFP_KERNEL);
+}
+
+static void *mtk_cam_smem_get_cpu_addr(struct mtk_cam_smem_dev *smem_dev,
+				       dma_addr_t addr)
+{
+	struct device *dev = smem_dev->dev;
+	struct dma_coherent_mem *dma_mem = dev->dma_mem;
+
+	if (addr < smem_dev->smem_base ||
+	    addr > smem_dev->smem_base + smem_dev->smem_size) {
+		dev_err(dev, "Invalid scp_addr %pad from sg\n", &addr);
+		return NULL;
+	}
+	return dma_mem->virt_base + (addr - smem_dev->smem_base);
+}
+
+static void mtk_cam_smem_sync_sg_for_cpu(struct device *dev,
+					 struct scatterlist *sgl, int nelems,
+					 enum dma_data_direction dir)
+{
+	struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev);
+	dma_addr_t scp_addr = sg_phys(sgl);
+	void *cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, scp_addr);
+
+	dev_dbg(dev,
+		"__dma_unmap_area:scp_addr:%pad,vaddr:%pK,size:%d,dir:%d\n",
+		&scp_addr, cpu_addr, sgl->length, dir);
+	__dma_unmap_area(cpu_addr, sgl->length, dir);
+}
+
+static void mtk_cam_smem_sync_sg_for_device(struct device *dev,
+					    struct scatterlist *sgl,
+					    int nelems,
+					    enum dma_data_direction dir)
+{
+	struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev);
+	dma_addr_t scp_addr = sg_phys(sgl);
+	void *cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, scp_addr);
+
+	dev_dbg(dev,
+		"__dma_map_area:scp_addr:%pad,vaddr:%pK,size:%d,dir:%d\n",
+		&scp_addr, cpu_addr, sgl->length, dir);
+	__dma_map_area(cpu_addr, sgl->length, dir);
+}
+
+static int mtk_cam_smem_setup_dma_ops(struct device *dev,
+				      struct dma_map_ops *smem_ops)
+{
+	memcpy((void *)smem_ops, dev->dma_ops, sizeof(*smem_ops));
+	smem_ops->get_sgtable = mtk_cam_smem_get_sgtable;
+	smem_ops->sync_sg_for_device = mtk_cam_smem_sync_sg_for_device;
+	smem_ops->sync_sg_for_cpu = mtk_cam_smem_sync_sg_for_cpu;
+	set_dma_ops(dev, smem_ops);
+
+	return 0;
+}
+
+static int mtk_cam_reserved_drm_sg_init(struct mtk_cam_smem_dev *smem_dev)
+{
+	u32 size_align, n_pages;
+	struct device *dev = smem_dev->dev;
+	struct sg_table *sgt = &smem_dev->sgt;
+	struct page **pages;
+	dma_addr_t dma_addr;
+	unsigned int i;
+	int ret;
+
+	size_align = PAGE_ALIGN(smem_dev->smem_size);
+	n_pages = size_align >> PAGE_SHIFT;
+
+	pages = kmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
+	if (!pages)
+		return -ENOMEM;
+
+	for (i = 0; i < n_pages; i++)
+		pages[i] = phys_to_page(smem_dev->smem_base + i * PAGE_SIZE);
+
+	ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0,
+					size_align, GFP_KERNEL);
+	if (ret) {
+		dev_err(dev, "failed to alloca sg table:%d\n", ret);
+		goto fail_table_alloc;
+	}
+	sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents,
+				      DMA_BIDIRECTIONAL,
+				      DMA_ATTR_SKIP_CPU_SYNC);
+	if (!sgt->nents) {
+		dev_err(dev, "failed to dma sg map\n");
+		goto fail_map;
+	}
+
+	dma_addr = sg_dma_address(sgt->sgl);
+	ret = dma_declare_coherent_memory(dev, smem_dev->smem_base,
+					  dma_addr, size_align,
+					  DMA_MEMORY_EXCLUSIVE);
+
+	if (ret)
+		dev_err(dev, "Unable to declare smem  memory:\n", ret);
+	else
+		dev_info(dev, "Coherent mem pa:%pad/%pad, size:%d\n",
+			 &smem_dev->smem_base, &dma_addr, size_align);
+
+	smem_dev->smem_size = size_align;
+	smem_dev->smem_pages = pages;
+	smem_dev->smem_dma_base = dma_addr;
+
+	return 0;
+
+fail_map:
+	sg_free_table(sgt);
+fail_table_alloc:
+	while (n_pages--)
+		__free_page(pages[n_pages]);
+	kfree(pages);
+	return -ENOMEM;
+}
+
+/* DMA memory related helper functions */
+static void mtk_cam_memdev_release(struct device *dev)
+{
+	vb2_dma_contig_clear_max_seg_size(dev);
+	of_reserved_mem_device_release(dev);
+}
+
+static struct device *mtk_cam_alloc_smem_dev(struct device *dev,
+					     const char *name)
+{
+	struct device *child;
+	int rc = 0;
+
+	child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL);
+	if (!child)
+		return NULL;
+
+	child->parent = dev;
+	child->iommu_group = dev->iommu_group;
+	child->release = mtk_cam_memdev_release;
+	dev_set_name(child, name);
+	set_dma_ops(child, get_dma_ops(dev));
+	child->dma_mask = dev->dma_mask;
+	rc = dma_set_coherent_mask(child, DMA_BIT_MASK(32));
+	vb2_dma_contig_set_max_seg_size(child, DMA_BIT_MASK(32));
+
+	if (device_register(child)) {
+		device_del(child);
+		return NULL;
+	}
+
+	return child;
+}
+
+int mtk_cam_reserved_memory_init(struct isp_p1_device *p1_dev)
+{
+	struct device *dev = &p1_dev->pdev->dev;
+	struct mtk_cam_smem_dev *smem_dev;
+
+	/* Allocate context */
+	smem_dev = devm_kzalloc(dev, sizeof(*smem_dev), GFP_KERNEL);
+	if (!smem_dev)
+		return -ENOMEM;
+
+	smem_dev->dev = mtk_cam_alloc_smem_dev(dev, "cam-smem");
+	if (!smem_dev->dev) {
+		dev_err(dev, "failed to alloc smem device\n");
+		return -ENODEV;
+	}
+	dev_set_drvdata(smem_dev->dev, smem_dev);
+	p1_dev->cam_dev->smem_dev = smem_dev->dev;
+
+	if (cam_reserved_smem) {
+		smem_dev->smem_base = cam_reserved_smem->base;
+		smem_dev->smem_size = cam_reserved_smem->size;
+		dev_info(dev, "%s dev:0x%pK base:%pad size:%u MiB\n",
+			 __func__,
+			 smem_dev->dev,
+			 &smem_dev->smem_base,
+			 (smem_dev->smem_size / SZ_1M));
+		mtk_cam_reserved_drm_sg_init(smem_dev);
+		mtk_cam_smem_setup_dma_ops(smem_dev->dev, &smem_dma_ops);
+	}
+
+	return 0;
+}
+
+static int __init mtk_cam_smem_dma_setup(struct reserved_mem *rmem)
+{
+	unsigned long node = rmem->fdt_node;
+
+	if (of_get_flat_dt_prop(node, "reusable", NULL))
+		return -EINVAL;
+
+	if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
+		pr_err("Reserved memory: no-map are not supported\n");
+		return -EINVAL;
+	}
+
+	cam_reserved_smem = rmem;
+
+	pr_info("%s:created DMA memory pool at %pa, size %u MiB\n",
+		__func__, &rmem->base, (rmem->size / SZ_1M));
+	return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(mtk_cam_smem,
+		       "mediatek,reserve-memory-cam-smem",
+		       mtk_cam_smem_dma_setup);
diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
new file mode 100644
index 000000000000..44fb876c87d5
--- /dev/null
+++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Frederic Chen <frederic.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CAM_ISP_SMEM_H
+#define __MTK_CAM_ISP_SMEM_H
+
+#include <linux/dma-mapping.h>
+
+#include "mtk_cam.h"
+
+int mtk_cam_reserved_memory_init(struct isp_p1_device *p1_dev);
+dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *smem_dev,
+					 dma_addr_t iova);
+
+#endif
+
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* Re: [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats
  2019-05-10  1:57 ` [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats Jungo Lin
@ 2019-05-13  8:35   ` Hans Verkuil
  2019-05-15 12:49     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Hans Verkuil @ 2019-05-13  8:35 UTC (permalink / raw)
  To: Jungo Lin, tfiga, hans.verkuil, laurent.pinchart+renesas,
	matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, sj.huang, yuzhao,
	linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

On 5/10/19 3:57 AM, Jungo Lin wrote:
> Add packed/unpacked/full-g bayer format with 8/10/12/14 bit
> for image output. Add Pass 1 (P1) specific meta formats for
> parameter processing and 3A/other statistics.

These pixel formats will need to be documented in Documentation/media/uapi/v4l/pixfmt-<something>.rst.

> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  include/uapi/linux/videodev2.h | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 1db220da3bcc..b79046d2d812 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -711,6 +711,20 @@ struct v4l2_pix_format {
>  #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
>  #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
>  
> +/* Vendor specific - Mediatek ISP compressed formats */
> +#define V4L2_PIX_FMT_MTISP_U8	v4l2_fourcc('M', 'T', 'U', '8') /* Unpacked bayer format, 16-bit */
> +#define V4L2_PIX_FMT_MTISP_U10  v4l2_fourcc('M', 'T', 'U', 'A') /* Unpacked bayer format, 16-bit */
> +#define V4L2_PIX_FMT_MTISP_U12  v4l2_fourcc('M', 'T', 'U', 'C') /* Unpacked bayer format, 16-bit */
> +#define V4L2_PIX_FMT_MTISP_U14  v4l2_fourcc('M', 'T', 'U', 'E') /* Unpacked bayer format, 16-bit */
> +#define V4L2_PIX_FMT_MTISP_B8	v4l2_fourcc('M', 'T', 'B', '8') /* Packed   bayer format,  8-bit */
> +#define V4L2_PIX_FMT_MTISP_B10  v4l2_fourcc('M', 'T', 'B', 'A') /* Packed   bayer format, 10-bit */
> +#define V4L2_PIX_FMT_MTISP_B12  v4l2_fourcc('M', 'T', 'B', 'C') /* Packed   bayer format, 12-bit */
> +#define V4L2_PIX_FMT_MTISP_B14  v4l2_fourcc('M', 'T', 'B', 'E') /* Packed   bayer format, 14-bit */
> +#define V4L2_PIX_FMT_MTISP_F8	v4l2_fourcc('M', 'T', 'F', '8') /* Full-G   bayer format,  8-bit */
> +#define V4L2_PIX_FMT_MTISP_F10  v4l2_fourcc('M', 'T', 'F', 'A') /* Full-G   bayer format, 10-bit */
> +#define V4L2_PIX_FMT_MTISP_F12  v4l2_fourcc('M', 'T', 'F', 'C') /* Full-G   bayer format, 12-bit */
> +#define V4L2_PIX_FMT_MTISP_F14  v4l2_fourcc('M', 'T', 'F', 'E') /* Full-G   bayer format, 14-bit */

Are these all compressed formats? What sort of compression is used? Can software unpack it,
or this is meant to be fed to other mediatek hardware blocks?

> +
>  /* SDR formats - used only for Software Defined Radio devices */
>  #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
>  #define V4L2_SDR_FMT_CU16LE       v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
> @@ -732,6 +746,12 @@ 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 */
>  #define V4L2_META_FMT_D4XX        v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
> +/* Vendor specific - Mediatek ISP parameters for firmware */
> +#define V4L2_META_FMT_MTISP_PARAMS v4l2_fourcc('M', 'T', 'f', 'p') /* ISP tuning parameters */
> +#define V4L2_META_FMT_MTISP_3A	   v4l2_fourcc('M', 'T', 'f', 'a') /* AE/AWB histogram */
> +#define V4L2_META_FMT_MTISP_AF	   v4l2_fourcc('M', 'T', 'f', 'f') /* AF histogram */
> +#define V4L2_META_FMT_MTISP_LCS	   v4l2_fourcc('M', 'T', 'f', 'c') /* Local contrast enhanced statistics */
> +#define V4L2_META_FMT_MTISP_LMV	   v4l2_fourcc('M', 'T', 'f', 'm') /* Local motion vector histogram */

The documentation for these meta formats either needs to point to
freely available mediatek documentation (i.e. no NDA needed), or it
has to be documented in a header or in the pixelformat documentation.

Regards,

	Hans

_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control
  2019-05-10  1:58 ` [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control Jungo Lin
@ 2019-05-13  8:46   ` Hans Verkuil
  2019-05-14  6:23     ` Jungo Lin
  2019-10-02 10:55     ` Sakari Ailus
  0 siblings, 2 replies; 104+ messages in thread
From: Hans Verkuil @ 2019-05-13  8:46 UTC (permalink / raw)
  To: Jungo Lin, tfiga, Sakari Ailus, laurent.pinchart+renesas,
	matthias.bgg, mchehab
  Cc: shik, devicetree, Sean.Cheng, suleiman, Rynn.Wu, srv_heupstream,
	ryan.yu, Jerry-ch.Chen, frankie.chiu, sj.huang, yuzhao,
	linux-mediatek, seraph.huang, zwisler, christie.yu,
	frederic.chen, linux-arm-kernel, linux-media

On 5/10/19 3:58 AM, Jungo Lin wrote:
> Reserved Mediatek ISP P1 private control number with 16.
> Moreover, add two private controls for ISP P1 user space
> usage.
> 
> 1. V4L2_CID_PRIVATE_GET_BIN_INFO
> - Provide the image output width & height in case
> camera binning mode is enabled.
> 
> 2. V4L2_CID_PRIVATE_RAW_PATH
> - Export the path control of the main stream to user space.
> One is pure raw and the other is processing raw.
> The default image path is pure raw.
> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c         | 133 ++++++++++++++++++
>  .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h         |  32 +++++
>  include/uapi/linux/v4l2-controls.h            |   4 +
>  3 files changed, 169 insertions(+)
>  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
>  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> 
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
> new file mode 100644
> index 000000000000..520adbe367ed
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
> @@ -0,0 +1,133 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ryan Yu <ryan.yu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */

Don't combine both SPDX and a license text. Just use the SPDX.

I see it being used elsewhere as well, so I won't repeat myself.

> +
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include "mtk_cam-dev.h"
> +#include "mtk_cam-ctrl.h"
> +#include "mtk_cam.h"
> +
> +static int handle_ctrl_get_bin_info(struct v4l2_ctrl *ctrl)
> +{
> +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> +	const unsigned int idx = MTK_CAM_P1_MAIN_STREAM_OUT;
> +	struct v4l2_format *imgo_fmt = &cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> +	unsigned int width, height;
> +
> +	width = imgo_fmt->fmt.pix_mp.width;
> +	height = imgo_fmt->fmt.pix_mp.height;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "Get bin info w*h:%d*%d",
> +		width, height);
> +
> +	ctrl->val = (width << 16) | height;
> +
> +	return 0;
> +}
> +
> +static int handle_ctrl_get_raw_path(struct v4l2_ctrl *ctrl)
> +{
> +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> +	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
> +
> +	ctrl->val = p1_dev->isp_ctx.isp_raw_path;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "Get raw path:%d", ctrl->val);
> +
> +	return 0;
> +}
> +
> +static int handle_ctrl_set_raw_path(struct v4l2_ctrl *ctrl)
> +{
> +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> +	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
> +
> +	p1_dev->isp_ctx.isp_raw_path = ctrl->val;
> +	dev_dbg(&cam_dev->pdev->dev, "Set raw path:%d", ctrl->val);
> +	return 0;
> +}
> +
> +static int mtk_cam_dev_g_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	switch (ctrl->id) {
> +	case V4L2_CID_PRIVATE_GET_BIN_INFO:
> +		handle_ctrl_get_bin_info(ctrl);
> +		break;
> +	case V4L2_CID_PRIVATE_RAW_PATH:
> +		handle_ctrl_get_raw_path(ctrl);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_cam_dev_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	switch (ctrl->id) {
> +	case V4L2_CID_PRIVATE_RAW_PATH:
> +		return handle_ctrl_set_raw_path(ctrl);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct v4l2_ctrl_ops mtk_cam_dev_ctrl_ops = {
> +	.g_volatile_ctrl = mtk_cam_dev_g_ctrl,
> +	.s_ctrl = mtk_cam_dev_s_ctrl,
> +};
> +
> +struct v4l2_ctrl_config mtk_cam_controls[] = {
> +	{
> +	.ops = &mtk_cam_dev_ctrl_ops,
> +	.id = V4L2_CID_PRIVATE_GET_BIN_INFO,

Don't use "PRIVATE" in the name. I'd replace that with MTK to indicate
that this is mediatek-specific. Same for the next control below.

> +	.name = "MTK CAM GET BIN INFO",
> +	.type = V4L2_CTRL_TYPE_INTEGER,
> +	.min = (IMG_MIN_WIDTH << 16) | IMG_MIN_HEIGHT,
> +	.max = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
> +	.step = 1,
> +	.def = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
> +	.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,

Don't mix width and height. I recommend splitting this into two controls.

Sakari might have an opinion on this as well.

> +	},
> +	{
> +	.ops = &mtk_cam_dev_ctrl_ops,
> +	.id = V4L2_CID_PRIVATE_RAW_PATH,
> +	.name = "MTK CAM RAW PATH",
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.min = 0,
> +	.max = 1,
> +	.step = 1,
> +	.def = 1,
> +	},

RAW_PATH is a very vague name. If it is 0, then it is pure raw, and if it
is 1, then it is 'processing raw'? If so, call it "Processing Raw".

Although you have to describe in the header or here what that means.

Private controls should be well documented.

> +};
> +
> +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
> +		      struct v4l2_ctrl_handler *hdl)
> +{
> +	unsigned int i;
> +
> +	/* Initialized HW controls, allow V4L2_CID_MTK_CAM_MAX ctrls */
> +	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_CAM_MAX);
> +	if (hdl->error) {
> +		v4l2_ctrl_handler_free(hdl);
> +		return hdl->error;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(mtk_cam_controls); i++)
> +		v4l2_ctrl_new_custom(hdl, &mtk_cam_controls[i], cam_dev);
> +
> +	dev_dbg(&cam_dev->pdev->dev, "%s done", __func__);
> +	return 0;
> +}
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> new file mode 100644
> index 000000000000..74a6538c81ac
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ryan Yu <ryan.yu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MTK_CAM_CTRL_H__
> +#define __MTK_CAM_CTRL_H__
> +
> +#include <media/v4l2-ctrls.h>
> +
> +#define V4L2_CID_MTK_CAM_PRIVATE_CAM  V4L2_CID_USER_MTK_CAM_BASE
> +#define V4L2_CID_PRIVATE_GET_BIN_INFO \
> +	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 1)
> +#define V4L2_CID_PRIVATE_RAW_PATH \
> +	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 2)

These last two defines can be on a single line.

They need to be documented in the header.

> +
> +#define V4L2_CID_MTK_CAM_MAX	16
> +
> +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
> +		      struct v4l2_ctrl_handler *hdl);
> +
> +#endif /* __MTK_CAM_CTRL_H__ */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 06479f2fb3ae..cbe8f5f7782b 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -192,6 +192,10 @@ enum v4l2_colorfx {
>   * We reserve 16 controls for this driver. */
>  #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
>  
> +/* The base for the mediatek ISP Pass 1 driver controls */
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_USER_MTK_CAM_BASE		(V4L2_CID_USER_BASE + 0x10c0)
> +
>  /* MPEG-class control IDs */
>  /* The MPEG controls are applicable to all codec controls
>   * and the 'MPEG' part of the define is historical */
> 

Regards,

	Hans

_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,07/11] media: platform: Add Mediatek ISP P1 private control
  2019-05-13  8:46   ` Hans Verkuil
@ 2019-05-14  6:23     ` Jungo Lin
  2019-10-02 10:55     ` Sakari Ailus
  1 sibling, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-14  6:23 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, frederic.chen, seraph.huang,
	linux-media, devicetree, zwisler, shik, yuzhao, linux-mediatek,
	matthias.bgg, mchehab, linux-arm-kernel, Sean.Cheng,
	srv_heupstream, sj.huang, tfiga, christie.yu, Sakari Ailus

Hi Hans,

Thank you for your comments.

On Mon, 2019-05-13 at 10:46 +0200, Hans Verkuil wrote:
> On 5/10/19 3:58 AM, Jungo Lin wrote:
> > Reserved Mediatek ISP P1 private control number with 16.
> > Moreover, add two private controls for ISP P1 user space
> > usage.
> > 
> > 1. V4L2_CID_PRIVATE_GET_BIN_INFO
> > - Provide the image output width & height in case
> > camera binning mode is enabled.
> > 
> > 2. V4L2_CID_PRIVATE_RAW_PATH
> > - Export the path control of the main stream to user space.
> > One is pure raw and the other is processing raw.
> > The default image path is pure raw.
> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c         | 133 ++++++++++++++++++
> >  .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h         |  32 +++++
> >  include/uapi/linux/v4l2-controls.h            |   4 +
> >  3 files changed, 169 insertions(+)
> >  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
> >  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> > 
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
> > new file mode 100644
> > index 000000000000..520adbe367ed
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
> > @@ -0,0 +1,133 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Ryan Yu <ryan.yu@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> 
> Don't combine both SPDX and a license text. Just use the SPDX.
> 
> I see it being used elsewhere as well, so I won't repeat myself.
> 

Ok, we will revise the license declaration and only keep SPDX license
only as below in all files.

// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2019 MediaTek Inc.

> > +
> > +#include <linux/device.h>
> > +#include <linux/platform_device.h>
> > +#include "mtk_cam-dev.h"
> > +#include "mtk_cam-ctrl.h"
> > +#include "mtk_cam.h"
> > +
> > +static int handle_ctrl_get_bin_info(struct v4l2_ctrl *ctrl)
> > +{
> > +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> > +	const unsigned int idx = MTK_CAM_P1_MAIN_STREAM_OUT;
> > +	struct v4l2_format *imgo_fmt = &cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> > +	unsigned int width, height;
> > +
> > +	width = imgo_fmt->fmt.pix_mp.width;
> > +	height = imgo_fmt->fmt.pix_mp.height;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "Get bin info w*h:%d*%d",
> > +		width, height);
> > +
> > +	ctrl->val = (width << 16) | height;
> > +
> > +	return 0;
> > +}
> > +
> > +static int handle_ctrl_get_raw_path(struct v4l2_ctrl *ctrl)
> > +{
> > +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> > +	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
> > +
> > +	ctrl->val = p1_dev->isp_ctx.isp_raw_path;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "Get raw path:%d", ctrl->val);
> > +
> > +	return 0;
> > +}
> > +
> > +static int handle_ctrl_set_raw_path(struct v4l2_ctrl *ctrl)
> > +{
> > +	struct mtk_cam_dev *cam_dev = ctrl->priv;
> > +	struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev);
> > +
> > +	p1_dev->isp_ctx.isp_raw_path = ctrl->val;
> > +	dev_dbg(&cam_dev->pdev->dev, "Set raw path:%d", ctrl->val);
> > +	return 0;
> > +}
> > +
> > +static int mtk_cam_dev_g_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	switch (ctrl->id) {
> > +	case V4L2_CID_PRIVATE_GET_BIN_INFO:
> > +		handle_ctrl_get_bin_info(ctrl);
> > +		break;
> > +	case V4L2_CID_PRIVATE_RAW_PATH:
> > +		handle_ctrl_get_raw_path(ctrl);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int mtk_cam_dev_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	switch (ctrl->id) {
> > +	case V4L2_CID_PRIVATE_RAW_PATH:
> > +		return handle_ctrl_set_raw_path(ctrl);
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static const struct v4l2_ctrl_ops mtk_cam_dev_ctrl_ops = {
> > +	.g_volatile_ctrl = mtk_cam_dev_g_ctrl,
> > +	.s_ctrl = mtk_cam_dev_s_ctrl,
> > +};
> > +
> > +struct v4l2_ctrl_config mtk_cam_controls[] = {
> > +	{
> > +	.ops = &mtk_cam_dev_ctrl_ops,
> > +	.id = V4L2_CID_PRIVATE_GET_BIN_INFO,
> 
> Don't use "PRIVATE" in the name. I'd replace that with MTK to indicate
> that this is mediatek-specific. Same for the next control below.
> 

We will adopt your suggestion and revise these naming in the next patch.

> > +	.name = "MTK CAM GET BIN INFO",
> > +	.type = V4L2_CTRL_TYPE_INTEGER,
> > +	.min = (IMG_MIN_WIDTH << 16) | IMG_MIN_HEIGHT,
> > +	.max = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
> > +	.step = 1,
> > +	.def = (IMG_MAX_WIDTH << 16) | IMG_MAX_HEIGHT,
> > +	.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
> 
> Don't mix width and height. I recommend splitting this into two controls.
> 
> Sakari might have an opinion on this as well.
> 

Ok, we will split this control into different two controls for width &
height usage.  

> > +	},
> > +	{
> > +	.ops = &mtk_cam_dev_ctrl_ops,
> > +	.id = V4L2_CID_PRIVATE_RAW_PATH,
> > +	.name = "MTK CAM RAW PATH",
> > +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +	},
> 
> RAW_PATH is a very vague name. If it is 0, then it is pure raw, and if it
> is 1, then it is 'processing raw'? If so, call it "Processing Raw".
> 
> Although you have to describe in the header or here what that means.
> 
> Private controls should be well documented.

Yes, you are right. We will rename this control to
V4L2_CID_MTK_PROCESSING_RAW and describes its usage in detail.

> 
> > +};
> > +
> > +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
> > +		      struct v4l2_ctrl_handler *hdl)
> > +{
> > +	unsigned int i;
> > +
> > +	/* Initialized HW controls, allow V4L2_CID_MTK_CAM_MAX ctrls */
> > +	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_CAM_MAX);
> > +	if (hdl->error) {
> > +		v4l2_ctrl_handler_free(hdl);
> > +		return hdl->error;
> > +	}
> > +
> > +	for (i = 0; i < ARRAY_SIZE(mtk_cam_controls); i++)
> > +		v4l2_ctrl_new_custom(hdl, &mtk_cam_controls[i], cam_dev);
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "%s done", __func__);
> > +	return 0;
> > +}
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> > new file mode 100644
> > index 000000000000..74a6538c81ac
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
> > @@ -0,0 +1,32 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Ryan Yu <ryan.yu@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef __MTK_CAM_CTRL_H__
> > +#define __MTK_CAM_CTRL_H__
> > +
> > +#include <media/v4l2-ctrls.h>
> > +
> > +#define V4L2_CID_MTK_CAM_PRIVATE_CAM  V4L2_CID_USER_MTK_CAM_BASE
> > +#define V4L2_CID_PRIVATE_GET_BIN_INFO \
> > +	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 1)
> > +#define V4L2_CID_PRIVATE_RAW_PATH \
> > +	(V4L2_CID_MTK_CAM_PRIVATE_CAM + 2)
> 
> These last two defines can be on a single line.
> 
> They need to be documented in the header.
> 

Ok, we will pay attenuation on this.
We will provide the detail information of these controls in next patch.

> > +
> > +#define V4L2_CID_MTK_CAM_MAX	16
> > +
> > +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev,
> > +		      struct v4l2_ctrl_handler *hdl);
> > +
> > +#endif /* __MTK_CAM_CTRL_H__ */
> > diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> > index 06479f2fb3ae..cbe8f5f7782b 100644
> > --- a/include/uapi/linux/v4l2-controls.h
> > +++ b/include/uapi/linux/v4l2-controls.h
> > @@ -192,6 +192,10 @@ enum v4l2_colorfx {
> >   * We reserve 16 controls for this driver. */
> >  #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
> >  
> > +/* The base for the mediatek ISP Pass 1 driver controls */
> > +/* We reserve 16 controls for this driver. */
> > +#define V4L2_CID_USER_MTK_CAM_BASE		(V4L2_CID_USER_BASE + 0x10c0)
> > +
> >  /* MPEG-class control IDs */
> >  /* The MPEG controls are applicable to all codec controls
> >   * and the 'MPEG' part of the define is historical */
> > 
> 
> Regards,
> 
> 	Hans

Best regards,

Jungo


_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
  2019-05-10  1:57 ` [RFC, V2, 01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory Jungo Lin
@ 2019-05-14 19:50   ` " Rob Herring
  2019-05-15 13:02     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Rob Herring @ 2019-05-14 19:50 UTC (permalink / raw)
  To: Jungo Lin
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

On Fri, May 10, 2019 at 09:57:47AM +0800, Jungo Lin wrote:
> This patch adds the binding for describing the reserved
> shared memory used to exchange ISP configuration and tuning
> data between the co-processor and Pass 1 (P1) unit of the
> camera ISP system on Mediatek SoCs.
> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  .../mediatek,reserve-memory-cam-smem.txt      | 42 +++++++++++++++++++
>  1 file changed, 42 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt

See my comments on the other 2 camera related reserved-memory bindings.

Rob

_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1
  2019-05-10  1:57 ` [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
@ 2019-05-14 19:54   ` Rob Herring
  2019-05-16  6:12     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Rob Herring @ 2019-05-14 19:54 UTC (permalink / raw)
  To: Jungo Lin
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

On Fri, May 10, 2019 at 09:57:52AM +0800, Jungo Lin wrote:
> This patch adds DT binding document for the Pass 1 (P1) unit in
> Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data
> out from the sensor interface, applies ISP image effects from tuning
> data and outputs the image data or statistics data to DRAM.
> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  .../bindings/media/mediatek,camisp.txt        | 92 +++++++++++++++++++
>  1 file changed, 92 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
> new file mode 100644
> index 000000000000..759e55a5dfac
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
> @@ -0,0 +1,92 @@
> +* Mediatek Image Signal Processor Pass 1 (ISP P1)
> +
> +The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out
> +from the sensor interface, applies ISP effects from tuning data and outputs
> +the image data and statistics data to DRAM. Furthermore, Pass 1 unit has
> +the ability to output two different resolutions frames at the same time to
> +increase the performance of the camera application.
> +
> +Required properties:
> +- compatible: Must be "mediatek,mt8183-camisp" for MT8183.
> +- reg: Must contain an entry for each entry in reg-names.

Must list reg-names here and define the values. Though, I don't find 
cam1, cam2, cam3 to be too useful.

> +- interrupts: interrupt number to the cpu.
> +- iommus: shall point to the respective IOMMU block with master port
> +  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
> +  for details.

How many entries?

> +- power-domains : a phandle to the power domain of this local arbiter.
> +- clocks: device clocks, see
> +  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> +- clock-names: must be "CAMSYS_CAM_CGPDN" and "CAMSYS_CAMTG_CGPDN".
> +- mediatek,larb: must contain the local arbiters in the current SOCs, see
> +  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
> +  for details.
> +- mediatek,scp : the node of system control processor (SCP), see
> +  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
> +- memory-region : the reserved shared memory region between Pass 1 unit and
> +  system control processor.
> +
> +Example:
> +SoC specific DT entry:
> +
> +	camisp: camisp@1a000000 {
> +		compatible = "mediatek,mt8183-camisp", "syscon";
> +		reg = <0 0x1a000000 0 0x1000>,
> +		      <0 0x1a003000 0 0x1000>,
> +		      <0 0x1a004000 0 0x2000>,
> +		      <0 0x1a006000 0 0x2000>;
> +		reg-names = "camisp",
> +		            "cam1",
> +		            "cam2",
> +		            "cam3";
> +		interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
> +			     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
> +			     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
> +		interrupt-names = "cam1",
> +				  "cam2",
> +				  "cam3";
> +		iommus = <&iommu M4U_PORT_CAM_LSCI0>,
> +			 <&iommu M4U_PORT_CAM_LSCI1>,
> +			 <&iommu M4U_PORT_CAM_BPCI>;
> +		#clock-cells = <1>;
> +		power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
> +		/* Camera CCF */
> +		clocks = <&camsys CLK_CAM_CAM>,
> +			 <&camsys CLK_CAM_CAMTG>;
> +		clock-names = "CAMSYS_CAM_CGPDN",
> +			      "CAMSYS_CAMTG_CGPDN";
> +		mediatek,larb = <&larb3>,
> +				<&larb6>;
> +		mediatek,scp = <&scp>;
> +		memory-region = <&cam_mem_reserved>;
> +	};
> +
> +Reserved memory specific DT entry (see reserved memory binding for more
> +information):
> +
> +Example:
> +SoC specific DT entry:
> +
> +	cam_mem_reserved: cam_mem_region {
> +		compatible = "mediatek,reserve-memory-cam-smem";
> +		no-map;
> +		size = <0 0x01400000>; / *20 MB share mem size */
> +		alignment = <0 0x1000>;
> +		alloc-ranges = <0 0x40000000 0 0x10000000>;
> +	};
> +
> +Mediatek ISP P1 supports a single port node with MIPI-CSI2 bus. It should
> +contain one 'port' child node with child 'endpoint' node. Please refer to
> +the bindings defined in Documentation/devicetree/bindings/media/video-interfaces.txt
> +and Documentation/devicetree/bindings/media/mediatek-seninf.txt.
> +
> +Example:
> +Board specific DT entry:

Don't split examples like this.

> +
> +	&camisp {
> +		port@0 {
> +			seninf_0: endpoint {
> +				remote-endpoint = <&seninf_core>;
> +			};
> +		};
> +	};
> +
> -- 
> 2.18.0
> 

_______________________________________________
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] 104+ messages in thread

* Re: [RFC, V2, 06/11] media: platform: Add Mediatek ISP P1 image & meta formats
  2019-05-13  8:35   ` Hans Verkuil
@ 2019-05-15 12:49     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-15 12:49 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, sj.huang, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, shik, tfiga, christie.yu, zwisler

Hi, Hans:

Thanks for your comments.

On Mon, 2019-05-13 at 10:35 +0200, Hans Verkuil wrote:
> On 5/10/19 3:57 AM, Jungo Lin wrote:
> > Add packed/unpacked/full-g bayer format with 8/10/12/14 bit
> > for image output. Add Pass 1 (P1) specific meta formats for
> > parameter processing and 3A/other statistics.
> 
> These pixel formats will need to be documented in Documentation/media/uapi/v4l/pixfmt-<something>.rst.
> 

Ok, we will add these pixfmt-<something>.rst files in next patch to
explain these pixel formats.

> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  include/uapi/linux/videodev2.h | 20 ++++++++++++++++++++
> >  1 file changed, 20 insertions(+)
> > 
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index 1db220da3bcc..b79046d2d812 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -711,6 +711,20 @@ struct v4l2_pix_format {
> >  #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
> >  #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
> >  
> > +/* Vendor specific - Mediatek ISP compressed formats */
> > +#define V4L2_PIX_FMT_MTISP_U8	v4l2_fourcc('M', 'T', 'U', '8') /* Unpacked bayer format, 16-bit */
> > +#define V4L2_PIX_FMT_MTISP_U10  v4l2_fourcc('M', 'T', 'U', 'A') /* Unpacked bayer format, 16-bit */
> > +#define V4L2_PIX_FMT_MTISP_U12  v4l2_fourcc('M', 'T', 'U', 'C') /* Unpacked bayer format, 16-bit */
> > +#define V4L2_PIX_FMT_MTISP_U14  v4l2_fourcc('M', 'T', 'U', 'E') /* Unpacked bayer format, 16-bit */
> > +#define V4L2_PIX_FMT_MTISP_B8	v4l2_fourcc('M', 'T', 'B', '8') /* Packed   bayer format,  8-bit */
> > +#define V4L2_PIX_FMT_MTISP_B10  v4l2_fourcc('M', 'T', 'B', 'A') /* Packed   bayer format, 10-bit */
> > +#define V4L2_PIX_FMT_MTISP_B12  v4l2_fourcc('M', 'T', 'B', 'C') /* Packed   bayer format, 12-bit */
> > +#define V4L2_PIX_FMT_MTISP_B14  v4l2_fourcc('M', 'T', 'B', 'E') /* Packed   bayer format, 14-bit */
> > +#define V4L2_PIX_FMT_MTISP_F8	v4l2_fourcc('M', 'T', 'F', '8') /* Full-G   bayer format,  8-bit */
> > +#define V4L2_PIX_FMT_MTISP_F10  v4l2_fourcc('M', 'T', 'F', 'A') /* Full-G   bayer format, 10-bit */
> > +#define V4L2_PIX_FMT_MTISP_F12  v4l2_fourcc('M', 'T', 'F', 'C') /* Full-G   bayer format, 12-bit */
> > +#define V4L2_PIX_FMT_MTISP_F14  v4l2_fourcc('M', 'T', 'F', 'E') /* Full-G   bayer format, 14-bit */
> 
> Are these all compressed formats? What sort of compression is used? Can software unpack it,
> or this is meant to be fed to other mediatek hardware blocks?
> 

No, these are not compressed formats. These images could be unpacked by
software, not depended on Mediatek hardware blocks.

> > +
> >  /* SDR formats - used only for Software Defined Radio devices */
> >  #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
> >  #define V4L2_SDR_FMT_CU16LE       v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
> > @@ -732,6 +746,12 @@ 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 */
> >  #define V4L2_META_FMT_D4XX        v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
> > +/* Vendor specific - Mediatek ISP parameters for firmware */
> > +#define V4L2_META_FMT_MTISP_PARAMS v4l2_fourcc('M', 'T', 'f', 'p') /* ISP tuning parameters */
> > +#define V4L2_META_FMT_MTISP_3A	   v4l2_fourcc('M', 'T', 'f', 'a') /* AE/AWB histogram */
> > +#define V4L2_META_FMT_MTISP_AF	   v4l2_fourcc('M', 'T', 'f', 'f') /* AF histogram */
> > +#define V4L2_META_FMT_MTISP_LCS	   v4l2_fourcc('M', 'T', 'f', 'c') /* Local contrast enhanced statistics */
> > +#define V4L2_META_FMT_MTISP_LMV	   v4l2_fourcc('M', 'T', 'f', 'm') /* Local motion vector histogram */
> 
> The documentation for these meta formats either needs to point to
> freely available mediatek documentation (i.e. no NDA needed), or it
> has to be documented in a header or in the pixelformat documentation.
> 

Ok, we are under internal discussion how to export these meta
information.

> Regards,
> 
> 	Hans
> 
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek



_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,01/11] dt-bindings: mt8183: Add binding for ISP Pass 1 reserved memory
  2019-05-14 19:50   ` [RFC,V2,01/11] " Rob Herring
@ 2019-05-15 13:02     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-15 13:02 UTC (permalink / raw)
  To: Rob Herring
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

Hi Rob:

Thanks for your comments.

On Tue, 2019-05-14 at 14:50 -0500, Rob Herring wrote:
> On Fri, May 10, 2019 at 09:57:47AM +0800, Jungo Lin wrote:
> > This patch adds the binding for describing the reserved
> > shared memory used to exchange ISP configuration and tuning
> > data between the co-processor and Pass 1 (P1) unit of the
> > camera ISP system on Mediatek SoCs.
> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  .../mediatek,reserve-memory-cam-smem.txt      | 42 +++++++++++++++++++
> >  1 file changed, 42 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/reserved-memory/mediatek,reserve-memory-cam-smem.txt
> 
> See my comments on the other 2 camera related reserved-memory bindings.
> 

Ok, we will align DIP & FD drivers's implementation.

Best regards,

Jungo

> Rob



_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,03/11] dt-bindings: mt8183: Added camera ISP Pass 1
  2019-05-14 19:54   ` Rob Herring
@ 2019-05-16  6:12     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-16  6:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: ryan.yu, Jerry-ch.Chen, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, frankie.chiu, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, sj.huang, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, shik, tfiga, christie.yu, zwisler

Hi, Rob:

Thanks for your comments.

On Tue, 2019-05-14 at 14:54 -0500, Rob Herring wrote:
> On Fri, May 10, 2019 at 09:57:52AM +0800, Jungo Lin wrote:
> > This patch adds DT binding document for the Pass 1 (P1) unit in
> > Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data
> > out from the sensor interface, applies ISP image effects from tuning
> > data and outputs the image data or statistics data to DRAM.
> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  .../bindings/media/mediatek,camisp.txt        | 92 +++++++++++++++++++
> >  1 file changed, 92 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
> > new file mode 100644
> > index 000000000000..759e55a5dfac
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
> > @@ -0,0 +1,92 @@
> > +* Mediatek Image Signal Processor Pass 1 (ISP P1)
> > +
> > +The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out
> > +from the sensor interface, applies ISP effects from tuning data and outputs
> > +the image data and statistics data to DRAM. Furthermore, Pass 1 unit has
> > +the ability to output two different resolutions frames at the same time to
> > +increase the performance of the camera application.
> > +
> > +Required properties:
> > +- compatible: Must be "mediatek,mt8183-camisp" for MT8183.
> > +- reg: Must contain an entry for each entry in reg-names.
> 
> Must list reg-names here and define the values. Though, I don't find 
> cam1, cam2, cam3 to be too useful.
> 

Ok, we will list all our supported reg-names in next patch.

> > +- interrupts: interrupt number to the cpu.
> > +- iommus: shall point to the respective IOMMU block with master port
> > +  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
> > +  for details.
> 
> How many entries?
> 

Basic, we just need to add only one master port for IOMMU property.
We will revise this and drop the other two ports.

> > +- power-domains : a phandle to the power domain of this local arbiter.
> > +- clocks: device clocks, see
> > +  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
> > +- clock-names: must be "CAMSYS_CAM_CGPDN" and "CAMSYS_CAMTG_CGPDN".
> > +- mediatek,larb: must contain the local arbiters in the current SOCs, see
> > +  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
> > +  for details.
> > +- mediatek,scp : the node of system control processor (SCP), see
> > +  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
> > +- memory-region : the reserved shared memory region between Pass 1 unit and
> > +  system control processor.
> > +
> > +Example:
> > +SoC specific DT entry:
> > +
> > +	camisp: camisp@1a000000 {
> > +		compatible = "mediatek,mt8183-camisp", "syscon";
> > +		reg = <0 0x1a000000 0 0x1000>,
> > +		      <0 0x1a003000 0 0x1000>,
> > +		      <0 0x1a004000 0 0x2000>,
> > +		      <0 0x1a006000 0 0x2000>;
> > +		reg-names = "camisp",
> > +		            "cam1",
> > +		            "cam2",
> > +		            "cam3";
> > +		interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
> > +			     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
> > +			     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
> > +		interrupt-names = "cam1",
> > +				  "cam2",
> > +				  "cam3";
> > +		iommus = <&iommu M4U_PORT_CAM_LSCI0>,
> > +			 <&iommu M4U_PORT_CAM_LSCI1>,
> > +			 <&iommu M4U_PORT_CAM_BPCI>;
> > +		#clock-cells = <1>;
> > +		power-domains = <&scpsys MT8183_POWER_DOMAIN_CAM>;
> > +		/* Camera CCF */
> > +		clocks = <&camsys CLK_CAM_CAM>,
> > +			 <&camsys CLK_CAM_CAMTG>;
> > +		clock-names = "CAMSYS_CAM_CGPDN",
> > +			      "CAMSYS_CAMTG_CGPDN";
> > +		mediatek,larb = <&larb3>,
> > +				<&larb6>;
> > +		mediatek,scp = <&scp>;
> > +		memory-region = <&cam_mem_reserved>;
> > +	};
> > +
> > +Reserved memory specific DT entry (see reserved memory binding for more
> > +information):
> > +
> > +Example:
> > +SoC specific DT entry:
> > +
> > +	cam_mem_reserved: cam_mem_region {
> > +		compatible = "mediatek,reserve-memory-cam-smem";
> > +		no-map;
> > +		size = <0 0x01400000>; / *20 MB share mem size */
> > +		alignment = <0 0x1000>;
> > +		alloc-ranges = <0 0x40000000 0 0x10000000>;
> > +	};
> > +
> > +Mediatek ISP P1 supports a single port node with MIPI-CSI2 bus. It should
> > +contain one 'port' child node with child 'endpoint' node. Please refer to
> > +the bindings defined in Documentation/devicetree/bindings/media/video-interfaces.txt
> > +and Documentation/devicetree/bindings/media/mediatek-seninf.txt.
> > +
> > +Example:
> > +Board specific DT entry:
> 
> Don't split examples like this.
> 

Ok, we will keep one example in next patch.

Best regards,

Jungo

> > +
> > +	&camisp {
> > +		port@0 {
> > +			seninf_0: endpoint {
> > +				remote-endpoint = <&seninf_core>;
> > +			};
> > +		};
> > +	};
> > +
> > -- 
> > 2.18.0
> > 
> 
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek



_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions
  2019-05-10  1:58 ` [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions Jungo Lin
@ 2019-05-24 18:49   ` Drew Davenport
  2019-05-28  1:00     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Drew Davenport @ 2019-05-24 18:49 UTC (permalink / raw)
  To: Jungo Lin
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

Hi Jungo,

On Fri, May 10, 2019 at 09:58:02AM +0800, Jungo Lin wrote:
> Implement standard V4L2 video driver that utilizes V4L2
> and media framework APIs. In this driver, supports one media
> device, one sub-device and six video devices during
> initialization. Moreover, it also connects with sensor and
> senif drivers with V4L2 async APIs.

Thanks for the patch. I've made a few comments inline. As a general
comment, what do you think of merging mtk_cam-dev.c and
mtk_cam-v4l2-util.c into one file? They seem to call into one another
and I'm not sure how beneficial it is to have them separate.

I have some comments on the other patches in this series that came about
while I was reviewing this, which I will send as well.

[snip]
 
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> new file mode 100644
> index 000000000000..5a581ab65945
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> @@ -0,0 +1,19 @@
> +#
> +# Copyright (C) 2018 MediaTek Inc.
> +#
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 2 as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +
> +mtk-cam-isp-objs += \
> +	mtk_cam.o mtk_cam-dev.o \
> +	mtk_cam-ctrl.o mtk_cam-scp.o \
> +	mtk_cam-v4l2-util.o mtk_cam-smem-dev.o

Some of these files are added in other patches. Consider adding files to
the Makefile in the same patch a file is added.

> +
> +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1_SUPPORT) += mtk-cam-isp.o
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
> new file mode 100644
> index 000000000000..dda8a7b161ee
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
> @@ -0,0 +1,758 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 Mediatek Corporation.
> + * Copyright (c) 2017 Intel Corporation.
> + * Copyright (C) 2017 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version
> + * 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-dev.h"
> +#include "mtk_cam-smem.h"
> +#include "mtk_cam-v4l2-util.h"
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
> +	.vidioc_querycap = mtk_cam_videoc_querycap,
> +	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
> +	.vidioc_enum_fmt_vid_cap_mplane = mtk_cam_videoc_enum_fmt,
> +	.vidioc_g_fmt_vid_cap_mplane = mtk_cam_videoc_g_fmt,
> +	.vidioc_s_fmt_vid_cap_mplane = mtk_cam_videoc_s_fmt,
> +	.vidioc_try_fmt_vid_cap_mplane = mtk_cam_videoc_try_fmt,
> +	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
> +	.vidioc_g_input = mtk_cam_vidioc_g_input,
> +	.vidioc_s_input = mtk_cam_vidioc_s_input,
> +	/* buffer queue management */
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_subscribe_event = mtk_cam_vidioc_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vout_ioctl_ops = {

This is not used anywhere. Please remove.

> +	.vidioc_querycap = mtk_cam_videoc_querycap,
> +	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
> +	.vidioc_enum_fmt_vid_out_mplane = mtk_cam_videoc_enum_fmt,
> +	.vidioc_g_fmt_vid_out_mplane = mtk_cam_videoc_g_fmt,
> +	.vidioc_s_fmt_vid_out_mplane = mtk_cam_videoc_s_fmt,
> +	.vidioc_try_fmt_vid_out_mplane = mtk_cam_videoc_try_fmt,
> +	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
> +	.vidioc_g_input = mtk_cam_vidioc_g_input,
> +	.vidioc_s_input = mtk_cam_vidioc_s_input,
> +	/* buffer queue management */
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +};
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
> +	.vidioc_querycap = mtk_cam_videoc_querycap,
> +	.vidioc_enum_fmt_meta_cap = mtk_cam_meta_enum_format,
> +	.vidioc_g_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_s_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_try_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +};
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
> +	.vidioc_querycap = mtk_cam_videoc_querycap,
> +	.vidioc_enum_fmt_meta_out = mtk_cam_meta_enum_format,
> +	.vidioc_g_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_s_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_try_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +};
> +
> +static struct v4l2_format meta_fmts[] = {
> +	{
> +		.fmt.meta = {
> +			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
> +			.buffersize = 128 * PAGE_SIZE,
> +		},
> +	},
> +	{
> +		.fmt.meta = {
> +			.dataformat = V4L2_META_FMT_MTISP_3A,
> +			.buffersize = 300 * PAGE_SIZE,
> +		},
> +	},
> +	{
> +		.fmt.meta = {
> +			.dataformat = V4L2_META_FMT_MTISP_AF,
> +			.buffersize = 160 * PAGE_SIZE,
> +		},
> +	},
> +	{
> +		.fmt.meta = {
> +			.dataformat = V4L2_META_FMT_MTISP_LCS,
> +			.buffersize = 72 * PAGE_SIZE,
> +		},
> +	},
> +	{
> +		.fmt.meta = {
> +			.dataformat = V4L2_META_FMT_MTISP_LMV,
> +			.buffersize = 256,
> +		},
> +	},
> +};
> +
> +/* Need to update mtk_cam_dev_fmt_set_img for default format configuration */
> +static struct v4l2_format stream_out_fmts[] = {
> +	{
> +		.fmt.pix_mp = {
> +			.width = IMG_MAX_WIDTH,
> +			.height = IMG_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_B8,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_SRGB,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = IMG_MAX_WIDTH,
> +			.height = IMG_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_SRGB,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = IMG_MAX_WIDTH,
> +			.height = IMG_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_B12,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_SRGB,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = IMG_MAX_WIDTH,
> +			.height = IMG_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_B14,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_SRGB,
> +			.num_planes = 1,
> +		},
> +	},
> +};
> +
> +static struct v4l2_format bin_out_fmts[] = {
> +	{
> +		.fmt.pix_mp = {
> +			.width = RRZ_MAX_WIDTH,
> +			.height = RRZ_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_F8,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_RAW,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = RRZ_MAX_WIDTH,
> +			.height = RRZ_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_RAW,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = RRZ_MAX_WIDTH,
> +			.height = RRZ_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_F12,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_RAW,
> +			.num_planes = 1,
> +		},
> +	},
> +	{
> +		.fmt.pix_mp = {
> +			.width = RRZ_MAX_WIDTH,
> +			.height = RRZ_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_F14,
> +			.field = V4L2_FIELD_NONE,
> +			.colorspace = V4L2_COLORSPACE_RAW,
> +			.num_planes = 1,
> +		},
> +	},
> +};
> +
> +static const struct
> +mtk_cam_dev_node_desc output_queues[MTK_CAM_P1_TOTAL_OUTPUT] = {
> +	{
> +		.id = MTK_CAM_P1_META_IN_0,
> +		.name = "meta input",
> +		.description = "ISP tuning parameters",
> +		.cap = V4L2_CAP_META_OUTPUT,
> +		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
> +		.link_flags = 0,
> +		.capture = false,
> +		.image = false,
> +		.smem_alloc = true,
> +		.fmts = meta_fmts,
> +		.num_fmts = ARRAY_SIZE(meta_fmts),
> +		.default_fmt_idx = 0,
> +		.max_buf_count = 10,
> +		.ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
> +	},
> +};
> +
> +static const struct
> +mtk_cam_dev_node_desc capture_queues[MTK_CAM_P1_TOTAL_CAPTURE] = {
> +	{
> +		.id = MTK_CAM_P1_MAIN_STREAM_OUT,
> +		.name = "main stream",
> +		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> +		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> +		.link_flags = 0,
> +		.capture = true,
> +		.image = true,
> +		.smem_alloc = false,
> +		.dma_port = R_IMGO,
> +		.fmts = stream_out_fmts,
> +		.num_fmts = ARRAY_SIZE(stream_out_fmts),
> +		.default_fmt_idx = 0,
> +		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> +	},
> +	{
> +		.id = MTK_CAM_P1_PACKED_BIN_OUT,
> +		.name = "packed out",
> +		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> +		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> +		.link_flags = 0,
> +		.capture = true,
> +		.image = true,
> +		.smem_alloc = false,
> +		.dma_port = R_RRZO,
> +		.fmts = bin_out_fmts,
> +		.num_fmts = ARRAY_SIZE(bin_out_fmts),
> +		.default_fmt_idx = 1,
> +		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> +	},
> +	{
> +		.id = MTK_CAM_P1_META_OUT_0,
> +		.name = "partial meta 0",
> +		.description = "AE/AWB histogram",
> +		.cap = V4L2_CAP_META_CAPTURE,
> +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> +		.link_flags = 0,
> +		.capture = true,
> +		.image = false,
> +		.smem_alloc = false,
> +		.dma_port = R_AAO | R_FLKO | R_PSO,
> +		.fmts = meta_fmts,
> +		.num_fmts = ARRAY_SIZE(meta_fmts),
> +		.default_fmt_idx = 1,
> +		.max_buf_count = 5,
> +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> +	},
> +	{
> +		.id = MTK_CAM_P1_META_OUT_1,
> +		.name = "partial meta 1",
> +		.description = "AF histogram",
> +		.cap = V4L2_CAP_META_CAPTURE,
> +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> +		.link_flags = 0,
> +		.capture = true,
> +		.image = false,
> +		.smem_alloc = false,
> +		.dma_port = R_AFO,
> +		.fmts = meta_fmts,
> +		.num_fmts = ARRAY_SIZE(meta_fmts),
> +		.default_fmt_idx = 2,
> +		.max_buf_count = 5,
> +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> +	},
> +	{
> +		.id = MTK_CAM_P1_META_OUT_2,
> +		.name = "partial meta 2",
> +		.description = "Local contrast enhanced statistics",
> +		.cap = V4L2_CAP_META_CAPTURE,
> +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> +		.link_flags = MEDIA_LNK_FL_DYNAMIC,
> +		.capture = true,
> +		.image = false,
> +		.smem_alloc = false,
> +		.dma_port = R_LCSO,
> +		.fmts = meta_fmts,
> +		.num_fmts = ARRAY_SIZE(meta_fmts),
> +		.default_fmt_idx = 3,
> +		.max_buf_count = 10,
> +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> +	},
> +	{
> +		.id = MTK_CAM_P1_META_OUT_3,
> +		.name = "partial meta 3",
> +		.description = "Local motion vector histogram",
> +		.cap = V4L2_CAP_META_CAPTURE,
> +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> +		.link_flags = MEDIA_LNK_FL_DYNAMIC,
> +		.capture = true,
> +		.image = false,
> +		.smem_alloc = false,
> +		.dma_port = R_LMVO,
> +		.fmts = meta_fmts,
> +		.num_fmts = ARRAY_SIZE(meta_fmts),
> +		.default_fmt_idx = 4,
> +		.max_buf_count = 10,
> +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> +	},
> +};
> +
> +static const struct mtk_cam_dev_queues_setting queues_setting = {
> +	.output_node_descs = output_queues,
> +	.total_output_nodes = MTK_CAM_P1_TOTAL_OUTPUT,
> +	.capture_node_descs = capture_queues,
> +	.total_capture_nodes = MTK_CAM_P1_TOTAL_CAPTURE,
> +};

I think this struct can be removed. See my comment in mtk_cam_dev_queue_setup

> +
> +static __u32 get_pixel_byte_by_fmt(__u32 pix_fmt)
> +{
> +	switch (pix_fmt) {
> +	case V4L2_PIX_FMT_MTISP_B8:
> +	case V4L2_PIX_FMT_MTISP_F8:
> +		return 8;
> +	case V4L2_PIX_FMT_MTISP_B10:
> +	case V4L2_PIX_FMT_MTISP_F10:
> +		return 10;
> +	case V4L2_PIX_FMT_MTISP_B12:
> +	case V4L2_PIX_FMT_MTISP_F12:
> +		return 12;
> +	case V4L2_PIX_FMT_MTISP_B14:
> +	case V4L2_PIX_FMT_MTISP_F14:
> +		return 14;
> +	case V4L2_PIX_FMT_MTISP_U8:
> +	case V4L2_PIX_FMT_MTISP_U10:
> +	case V4L2_PIX_FMT_MTISP_U12:
> +	case V4L2_PIX_FMT_MTISP_U14:
> +		return 16;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static __u32 align_main_stream_size(__u32 size, unsigned int pix_mode)

Since only one_pixel_mode is supported, this function can be removed and
the callsite replaced with ALIGN(size, 2). This function can be added
once more when other pixel modes are supported.

> +{
> +	switch (pix_mode) {
> +	case default_pixel_mode:
> +	case four_pixel_mode:
> +		return ALIGN(size, 8);
> +	case two_pixel_mode:
> +		return ALIGN(size, 4);
> +	case one_pixel_mode:
> +		return ALIGN(size, 2);
> +	default:
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static unsigned int align_packetd_out_size(__u32 size,
> +					   unsigned int pix_mode,
> +					   __u32 fmt)

This is only ever called with one_pixel_mode. Remove the pix_mode
argument and unreachable code.

> +{
> +	switch (pix_mode) {
> +	case default_pixel_mode:
> +	case four_pixel_mode:
> +		return ALIGN(size, 16);
> +	case two_pixel_mode:
> +		return ALIGN(size, 8);
> +	case one_pixel_mode:
> +		if (fmt == V4L2_PIX_FMT_MTISP_F10)
> +			return ALIGN(size, 4);
> +		else
> +			return ALIGN(size, 8);
> +	default:
> +		return ALIGN(size, 16);
> +	}
> +	return 0;
> +}
> +
> +static __u32 cal_main_stream_stride(struct device *dev,
> +				    __u32 width,
> +				    __u32 pix_fmt,
> +				    __u32 pix_mode)

This function is only called with one_pixel_mode. Remove the pix_mode
argument.

> +{
> +	__u32 stride;
> +	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
> +
> +	width = ALIGN(width, 4);
> +	stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2);
> +	/* expand stride, instead of shrink width */
> +	stride = align_main_stream_size(stride, pix_mode);
> +
> +	dev_dbg(dev,
> +		"main width:%d, pix_mode:%d, stride:%d\n",
> +		width, pix_mode, stride);
> +	return stride;
> +}
> +
> +static __u32 cal_packed_out_stride(struct device *dev,
> +				   __u32 width,
> +				   __u32 pix_fmt,
> +				   __u32 pix_mode)

This function is only called with one_pixel_mode. Remove the pix_mode
argument.

> +{
> +	__u32 stride;
> +	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
> +
> +	width = ALIGN(width, 4);
> +	stride = DIV_ROUND_UP(width * 3, 2);
> +	stride = DIV_ROUND_UP(stride * pixel_byte, 8);
> +	/* expand stride, instead of shrink width */
> +	stride = align_packetd_out_size(stride, pix_mode, pix_fmt);
> +
> +	dev_dbg(dev,
> +		"packed width:%d, pix_mode:%d, stride:%d\n",
> +		width, pix_mode, stride);
> +
> +	return stride;
> +}
> +
> +static __u32 cal_img_stride(struct device *dev,
> +			    int node_id,
> +			    __u32 width,
> +			    __u32 pix_fmt)
> +{
> +	__u32 bpl;
> +
> +	/* Currently, only support one_pixel_mode */
> +	if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT)
> +		bpl = cal_main_stream_stride(dev, width, pix_fmt,
> +					     one_pixel_mode);
> +	else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT)
> +		bpl = cal_packed_out_stride(dev, width, pix_fmt,
> +					    one_pixel_mode);
> +
> +	/* For DIP HW constrained, it needs 4 byte alignment */
> +	bpl = ALIGN(bpl, 4);
> +
> +	return bpl;
> +}
> +
> +struct v4l2_format *
> +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
> +{
> +	unsigned int i;
> +	struct v4l2_format *dev_fmt;
> +
> +	for (i = 0; i < desc->num_fmts; i++) {
> +		dev_fmt = &desc->fmts[i];
> +		if (dev_fmt->fmt.pix_mp.pixelformat == format)
> +			return dev_fmt;
> +	}
> +
> +	return NULL;
> +}
> +
> +/* The helper to configure the device context */
> +void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
> +			     const struct mtk_cam_dev_queues_setting *setting)

This is only ever called with the same mtk_cam_dev_queues_setting
struct. I think you can remove that struct altogether and just set the
mtk_cam_dev_node_desc* for each node from output_queues and
capture_queues directly.

Also this can be a static function.

> +{
> +	unsigned int i, node_idx;
> +
> +	node_idx = 0;
> +
> +	/* Setup the output queue */
> +	for (i = 0; i < setting->total_output_nodes; i++)
> +		cam_dev->mem2mem2_nodes[node_idx++].desc =
> +			setting->output_node_descs[i];
> +
> +	/* Setup the capture queue */
> +	for (i = 0; i < setting->total_capture_nodes; i++)
> +		cam_dev->mem2mem2_nodes[node_idx++].desc =
> +			setting->capture_node_descs[i];
> +
> +	cam_dev->dev_node_num = node_idx;

This value is known at compile time (MTK_CAM_P1_TOTAL_OUTPUT +
MTK_CAM_P1_TOTAL_CAPTURE). Can we just #define that constant and use
that instead of dev_node_num?

> +}
> +
> +int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
> +			   struct mtk_cam_dev_finish_param *fram_param)
> +{
> +	struct mtk_cam_dev_buffer *buf, *b0;
> +
> +	if (!cam_dev->streaming)
> +		return 0;
> +
> +	dev_dbg(&cam_dev->pdev->dev,
> +		"job recvied request fd:%d, frame_seq:%d state:%d\n",
> +		fram_param->request_fd,
> +		fram_param->frame_seq_no,
> +		fram_param->state);
> +
> +	/*
> +	 * Set the buffer's VB2 status so that the user can dequeue
> +	 * the buffer.
> +	 */
> +	list_for_each_entry_safe(buf, b0, fram_param->list_buf, list) {
> +		list_del(&buf->list);
> +		buf->vbb.vb2_buf.timestamp = ktime_get_ns();
> +		buf->vbb.sequence = fram_param->frame_seq_no;
> +		if (buf->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
> +			vb2_buffer_done(&buf->vbb.vb2_buf, fram_param->state);
> +	}
> +
> +	return 0;
> +}
> +
> +int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
> +				 __u32 frame_seq_no)
> +{
> +	struct v4l2_event event;
> +
> +	memset(&event, 0, sizeof(event));
> +	event.type = V4L2_EVENT_FRAME_SYNC;
> +	event.u.frame_sync.frame_sequence = frame_seq_no;
> +	v4l2_event_queue(cam_dev->subdev.devnode, &event);
> +
> +	return 0;
> +}
> +
> +/* Calcuate mplane pix format */
> +void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
> +				    struct v4l2_pix_format_mplane *dest_fmt,
> +				    unsigned int node_id)
> +{
> +	unsigned int i;
> +	__u32 bpl, sizeimage, imagsize;
> +
> +	imagsize = 0;
> +	for (i = 0 ; i < dest_fmt->num_planes; ++i) {
> +		bpl = cal_img_stride(dev,
> +				     node_id,
> +				     dest_fmt->width,
> +				     dest_fmt->pixelformat);
> +		sizeimage = bpl * dest_fmt->height;
> +		imagsize += sizeimage;
> +		dest_fmt->plane_fmt[i].bytesperline = bpl;
> +		dest_fmt->plane_fmt[i].sizeimage = sizeimage;
> +		memset(dest_fmt->plane_fmt[i].reserved,
> +		       0, sizeof(dest_fmt->plane_fmt[i].reserved));
> +		dev_dbg(dev, "plane:%d,bpl:%d,sizeimage:%u\n",
> +			i,  bpl, dest_fmt->plane_fmt[i].sizeimage);
> +	}
> +
> +	if (dest_fmt->num_planes == 1)
> +		dest_fmt->plane_fmt[0].sizeimage = imagsize;
> +}
> +
> +void mtk_cam_dev_fmt_set_img(struct device *dev,
> +			     struct v4l2_pix_format_mplane *dest_fmt,
> +			     struct v4l2_pix_format_mplane *src_fmt,
> +			     unsigned int node_id)
> +{
> +	dest_fmt->width = src_fmt->width;
> +	dest_fmt->height = src_fmt->height;
> +	dest_fmt->pixelformat = src_fmt->pixelformat;
> +	dest_fmt->field = src_fmt->field;
> +	dest_fmt->colorspace = src_fmt->colorspace;
> +	dest_fmt->num_planes = src_fmt->num_planes;
> +	/* Use default */
> +	dest_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +	dest_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> +	dest_fmt->xfer_func =
> +		V4L2_MAP_XFER_FUNC_DEFAULT(dest_fmt->colorspace);
> +	memset(dest_fmt->reserved, 0, sizeof(dest_fmt->reserved));
> +
> +	dev_dbg(dev, "%s: Dest Fmt:%c%c%c%c, w*h:%d*%d\n",
> +		__func__,
> +		(dest_fmt->pixelformat & 0xFF),
> +		(dest_fmt->pixelformat >> 8) & 0xFF,
> +		(dest_fmt->pixelformat >> 16) & 0xFF,
> +		(dest_fmt->pixelformat >> 24) & 0xFF,
> +		dest_fmt->width,
> +		dest_fmt->height);
> +
> +	mtk_cam_dev_cal_mplane_pix_fmt(dev, dest_fmt, node_id);
> +}
> +
> +/* Get the default format setting */
> +void mtk_cam_dev_load_default_fmt(struct device *dev,
> +				  struct mtk_cam_dev_node_desc *queue_desc,
> +				  struct v4l2_format *dest)
> +{
> +	struct v4l2_format *default_fmt =
> +		&queue_desc->fmts[queue_desc->default_fmt_idx];
> +
> +	dest->type = queue_desc->buf_type;
> +
> +	/* Configure default format based on node type */
> +	if (queue_desc->image) {
> +		mtk_cam_dev_fmt_set_img(dev,
> +					&dest->fmt.pix_mp,
> +					&default_fmt->fmt.pix_mp,
> +					queue_desc->id);
> +	} else {
> +		dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
> +		dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
> +	}
> +}
> +
> +/* Get a free buffer from a video node */
> +static struct mtk_cam_dev_buffer *
> +mtk_cam_dev_get_pending_buf(struct mtk_cam_dev *cam_dev, int node)
> +{
> +	struct mtk_cam_dev_buffer *buf;
> +	struct mtk_cam_video_device *vdev;
> +
> +	if (node > cam_dev->dev_node_num || node < 0) {
> +		dev_err(&cam_dev->pdev->dev, "Invalid mtk_cam_dev node.\n");
> +		return NULL;
> +	}
> +	vdev = &cam_dev->mem2mem2_nodes[node];
> +
> +	spin_lock(&vdev->slock);
> +	buf = list_first_entry_or_null(&vdev->pending_list,
> +				       struct mtk_cam_dev_buffer,
> +				       list);
> +	if (!buf) {
> +		spin_unlock(&vdev->slock);
> +		return NULL;
> +	}
> +	list_del(&buf->list);
> +	spin_unlock(&vdev->slock);

Can this be simplified by going:
spin_lock();
buf = list_first_entry_or_null(...);
if (buf) list_del(...);
spin_unlock();
return buf;

> +
> +	return buf;
> +}
> +
> +int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev)

This only ever returns 0, so make it a void function.

> +{
> +	unsigned int node;
> +	const int mtk_cam_dev_node_num = cam_dev->dev_node_num;
> +	struct device *dev = &cam_dev->pdev->dev;
> +	struct mtk_cam_dev_start_param s_param;
> +	struct mtk_cam_dev_buffer *buf;
> +
> +	memset(&s_param, 0, sizeof(struct mtk_cam_dev_start_param));
> +
> +	if (!cam_dev->streaming) {
> +		dev_dbg(dev, "%s: stream off, no enqueue\n", __func__);
> +		return 0;
> +	}
> +
> +	/* Check all enabled nodes to collect its buffer  */
> +	for (node = 0; node < mtk_cam_dev_node_num; node++) {
> +		if (!cam_dev->mem2mem2_nodes[node].enabled)
> +			continue;
> +		buf = mtk_cam_dev_get_pending_buf(cam_dev, node);
> +		if (!buf)
> +			continue;
> +
> +		/* TBD: use buf_init callback function */
> +		buf->daddr =
> +			vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
> +		if (cam_dev->mem2mem2_nodes[node].desc.smem_alloc) {
> +			buf->scp_addr = mtk_cam_smem_iova_to_scp_addr(
> +				cam_dev->smem_dev, buf->daddr);
> +		} else {
> +			buf->scp_addr = 0;
> +		}
> +
> +		dev_dbg(dev,
> +			"Node:%d fd:%d idx:%d state:%d daddr:%pad addr:%pad",
> +			node,
> +			buf->vbb.request_fd,
> +			buf->vbb.vb2_buf.index,
> +			buf->vbb.vb2_buf.state,
> +			&buf->daddr,
> +			&buf->scp_addr);
> +
> +		s_param.buffers[node] = buf;
> +		s_param.request_fd = buf->vbb.request_fd;
> +	}
> +
> +	/* Trigger en-queued job to driver */
> +	mtk_isp_req_enqueue(dev, &s_param);
> +
> +	return 0;
> +}
> +
> +int mtk_cam_dev_init(struct platform_device *pdev,
> +		     struct mtk_cam_dev *cam_dev)
> +{
> +	int ret;
> +
> +	cam_dev->pdev = pdev;
> +
> +	mtk_cam_dev_queue_setup(cam_dev, &queues_setting);
> +
> +	/* v4l2 sub-device registration */
> +	dev_dbg(&cam_dev->pdev->dev, "mem2mem2.name: %s\n",
> +		MTK_CAM_DEV_P1_NAME);
> +
> +	ret = mtk_cam_mem2mem2_v4l2_register(cam_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = mtk_cam_v4l2_async_register(cam_dev);
> +	if (ret)

If this fails do we need to undo the stuff done in
mtk_cam_mem2mem2_v4l2_register?

> +		return ret;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_dev_release(struct platform_device *pdev,
> +			struct mtk_cam_dev *cam_dev)
> +{
> +	mtk_cam_v4l2_async_unregister(cam_dev);
> +	mtk_cam_v4l2_unregister(cam_dev);
> +
> +	return 0;
> +}
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
> new file mode 100644
> index 000000000000..410460de44fa
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
> @@ -0,0 +1,250 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 Mediatek Corporation.
> + * Copyright (c) 2017 Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version
> + * 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
> + *
> + */
> +
> +#ifndef __MTK_CAM_DEV_H__
> +#define __MTK_CAM_DEV_H__
> +
> +#include <linux/device.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +#define MTK_CAM_DEV_P1_NAME		"MTK-ISP-P1-V4L2"
> +
> +#define MTK_CAM_DEV_NODES		11
> +
> +#define MTK_CAM_P1_META_IN_0		0
> +#define MTK_CAM_P1_TOTAL_OUTPUT		1
> +
> +#define MTK_CAM_P1_MAIN_STREAM_OUT	1
> +#define MTK_CAM_P1_PACKED_BIN_OUT	2
> +#define MTK_CAM_P1_META_OUT_0		3
> +#define MTK_CAM_P1_META_OUT_1		4
> +#define MTK_CAM_P1_META_OUT_2		5
> +#define MTK_CAM_P1_META_OUT_3		6
> +#define MTK_CAM_P1_TOTAL_CAPTURE	6

Please align macro values using tabs.

> +
> +struct mtk_cam_dev_buffer {
> +	struct vb2_v4l2_buffer	vbb;
> +	struct list_head	list;
> +	/* Intenal part */
> +	dma_addr_t		daddr;
> +	dma_addr_t		scp_addr;
> +};
> +
> +/* Attributes setup by device owner */
> +struct mtk_cam_dev_queues_setting {
> +	const struct mtk_cam_dev_node_desc *output_node_descs;
> +	unsigned int total_output_nodes;
> +	const struct mtk_cam_dev_node_desc *capture_node_descs;
> +	unsigned int total_capture_nodes;
> +};
> +
> +struct mtk_cam_dev_start_param {
> +	int request_fd;
> +	struct mtk_cam_dev_buffer *buffers[MTK_CAM_DEV_NODES];
> +};
> +
> +struct mtk_cam_dev_finish_param {
> +	int request_fd;
> +	unsigned int frame_seq_no;
> +	unsigned int state;
> +	struct list_head *list_buf;
> +};
> +
> +/*
> + * struct mtk_cam_dev_node_desc - node attributes
> + *
> + * @id:		 id of the context queue
> + * @name:	 media entity name
> + * @description: descritpion of node
> + * @cap:	 mapped to V4L2 capabilities
> + * @buf_type:	 mapped to V4L2 buffer type
> + * @dma_port:	 the dma port associated to the buffer
> + * @link_flags:	 default media link flags
> + * @smem_alloc:	 using the cam_smem_drv as alloc ctx or not
> + * @capture:	 true for capture queue (device to user)
> + *		 false for output queue (from user to device)
> + * @image:	 true for image node, false for meta node
> + * @num_fmts:	 the number of supported formats
> + * @default_fmt_idx: default format of this node
> + * @max_buf_count: maximum V4L2 buffer count
> + * @ioctl_ops:  mapped to v4l2_ioctl_ops
> + * @fmts:	supported format
> + *
> + */
> +struct mtk_cam_dev_node_desc {
> +	u8 id;
> +	char *name;
> +	char *description;
> +	u32 cap;
> +	u32 buf_type;
> +	u32 dma_port;
> +	u32 link_flags;
> +	u8 smem_alloc:1;
> +	u8 capture:1;
> +	u8 image:1;
> +	u8 num_fmts;
> +	u8 default_fmt_idx;
> +	u8 max_buf_count;
> +	const struct v4l2_ioctl_ops *ioctl_ops;
> +	struct v4l2_format *fmts;
> +};
> +
> +/*
> + * struct mtk_cam_video_device - Mediatek video device structure.
> + *
> + * @id:		Id for mtk_cam_dev_node_desc or mem2mem2_nodes array
> + * @enabled:	Indicate the device is enabled or not
> + * @vdev_fmt:	The V4L2 format of video device
> + * @vdev_apd:	The media pad graph object of video device
> + * @vbq:	A videobuf queue of video device
> + * @desc:	The node attributes of video device
> + * @ctrl_handler:	The control handler of video device
> + * @pending_list:	List for pending buffers before enqueuing into driver
> + * @lock:	Serializes vb2 queue and video device operations.
> + * @slock:	Protect for pending_list.
> + *
> + */
> +struct mtk_cam_video_device {
> +	unsigned int id;
> +	unsigned int enabled;
> +	struct v4l2_format vdev_fmt;
> +	struct video_device vdev;
> +	struct media_pad vdev_pad;
> +	struct vb2_queue vbq;
> +	struct mtk_cam_dev_node_desc desc;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct list_head pending_list;
> +	/* Used for vbq & vdev */
> +	struct mutex lock;
> +	/* protect for pending_list */
> +	spinlock_t slock;
> +};
> +
> +/*
> + * struct mtk_cam_dev - Mediatek camera device structure.
> + *
> + * @pdev:	Pointer to platform device
> + * @smem_pdev:	Pointer to shared memory platform device
> + * @pipeline:	Media pipeline information
> + * @media_dev:	Media device
> + * @subdev:	The V4L2 sub-device
> + * @v4l2_dev:	The V4L2 device driver
> + * @notifier:	The v4l2_device notifier data
> + * @subdev_pads: Pointer to the number of media pads of this sub-device
> + * @ctrl_handler: The control handler
> + * @mem2mem2_nodes: The array list of mtk_cam_video_device
> + * @seninf:	Pointer to the seninf sub-device
> + * @sensor:	Pointer to the active sensor V4L2 sub-device when streaming on
> + * @streaming:	Indicate the overall streaming status is on or off
> + * @dev_node_num: The number of supported V4L2 video device nodes
> + * @request_fd:	The file descriptor of request API
> + * @request_count: The buffer count of request API
> + *
> + * Below is the graph topology for Camera IO connection.
> + * sensor 1 (main) --> sensor IF --> P1 sub-device
> + * sensor 2 (sub)  -->
> + *
> + */
> +struct mtk_cam_dev {
> +	struct platform_device *pdev;
> +	struct device *smem_dev;
> +	struct media_pipeline pipeline;
> +	struct media_device media_dev;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_device v4l2_dev;
> +	struct v4l2_async_notifier notifier;
> +	struct media_pad *subdev_pads;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct mtk_cam_video_device mem2mem2_nodes[MTK_CAM_DEV_NODES];
> +	struct v4l2_subdev *seninf;
> +	struct v4l2_subdev *sensor;
> +	unsigned int streaming;
> +	unsigned int dev_node_num;
> +	int request_fd;
> +	unsigned int request_count;
> +};
> +
> +int mtk_cam_dev_init(struct platform_device *pdev,
> +		     struct mtk_cam_dev *cam_dev);
> +int mtk_cam_v4l2_register(struct device *dev,
> +			  struct media_device *media_dev,
> +			  struct v4l2_device *v4l2_dev,
> +			  struct v4l2_ctrl_handler *ctrl_handler);
> +int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev);
> +int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev);
> +int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev);
> +void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev);
> +int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev);
> +int mtk_cam_dev_release(struct platform_device *pdev,
> +			struct mtk_cam_dev *cam_dev);
> +void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
> +			     const struct mtk_cam_dev_queues_setting *setting);
> +int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
> +			   struct mtk_cam_dev_finish_param *param);
> +int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
> +				 __u32 frame_seq_no);
> +void mtk_cam_dev_fmt_set_img(struct device *dev,
> +			     struct v4l2_pix_format_mplane *dest_fmt,
> +			     struct v4l2_pix_format_mplane *src_fmt,
> +			     unsigned int node_id);
> +struct v4l2_format *
> +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *queue_desc, u32 format);
> +void mtk_cam_dev_load_default_fmt(struct device *dev,
> +				  struct mtk_cam_dev_node_desc *queue,
> +				  struct v4l2_format *dest_fmt);
> +void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
> +				    struct v4l2_pix_format_mplane *dest_fmt,
> +				    unsigned int node_id);
> +
> +static inline struct mtk_cam_video_device *
> +file_to_mtk_cam_node(struct file *__file)
> +{
> +	return container_of(video_devdata(__file),
> +		struct mtk_cam_video_device, vdev);
> +}
> +
> +static inline struct mtk_cam_dev *
> +mtk_cam_subdev_to_dev(struct v4l2_subdev *__sd)
> +{
> +	return container_of(__sd,
> +		struct mtk_cam_dev, subdev);
> +}
> +
> +static inline struct mtk_cam_video_device *
> +mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
> +{
> +	return container_of(__vq,
> +		struct mtk_cam_video_device, vbq);
> +}
> +
> +static inline struct mtk_cam_dev_buffer *
> +mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
> +{
> +	return container_of(__vb,
> +		struct mtk_cam_dev_buffer, vbb.vb2_buf);
> +}
> +
> +#endif /* __MTK_CAM_DEV_H__ */
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
> new file mode 100644
> index 000000000000..196aaef3d854
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
> @@ -0,0 +1,1086 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 Mediatek Corporation.
> + * Copyright (c) 2017 Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version
> + * 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * MTK_CAM-v4l2 is highly based on Intel IPU3 ImgU driver.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_graph.h>
> +#include <media/v4l2-common.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-ctrl.h"
> +#include "mtk_cam-dev.h"
> +#include "mtk_cam-v4l2-util.h"
> +
> +#define MTK_CAM_SENINF_PAD_SRC			4
> +#define MTK_CAM_P1_HUB_PAD_SINK			MTK_CAM_DEV_NODES
> +
> +static int mtk_cam_subdev_open(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_fh *fh)
> +{
> +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> +
> +	cam_dev->request_fd = -1;
> +	cam_dev->request_count = 0;
> +
> +	return mtk_isp_open(&cam_dev->pdev->dev);
> +}
> +
> +static int mtk_cam_subdev_close(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh)
> +{
> +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> +
> +	return mtk_isp_release(&cam_dev->pdev->dev);
> +}
> +
> +static int mtk_cam_v4l2_get_active_sensor(struct mtk_cam_dev *cam_dev)

"get" implies that this function will retrieve something without
side effects, which is not the case here. In the error case, the return
value is ignored by the caller as well.

Consider making this function return a struct v4l2_subdev* (or NULL in
the error case) and let the caller set mtk_cam_dev::sensor.

> +{
> +	struct media_device *mdev = cam_dev->seninf->entity.graph_obj.mdev;
> +	struct media_entity *entity;
> +	struct device *dev = &cam_dev->pdev->dev;
> +
> +	cam_dev->sensor = NULL;
> +	media_device_for_each_entity(entity, mdev) {
> +		dev_dbg(dev, "media entity: %s:0x%x\n",
> +			entity->name, entity->function);
> +		if (entity->function == MEDIA_ENT_F_CAM_SENSOR &&
> +		    entity->stream_count > 0) {
> +			cam_dev->sensor = media_entity_to_v4l2_subdev(entity);
> +			dev_dbg(dev, "Sensor found: %s\n", entity->name);
> +			break;
> +		}
> +	}
> +
> +	if (!cam_dev->sensor) {
> +		dev_err(dev, "Sensor is not connected\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam_dev)
> +{
> +	struct device *dev = &cam_dev->pdev->dev;
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	int ret;
> +
> +	/* Align vb2_core_streamon design */
> +	if (cam_dev->streaming) {
> +		dev_warn(dev, "already streaming\n", dev);
> +		return 0;
> +	}
> +
> +	if (!cam_dev->seninf) {
> +		dev_err(dev, "no seninf connected:%d\n", ret);
> +		return -EPERM;
> +	}
> +
> +	/* Get active sensor from graph topology */
> +	ret = mtk_cam_v4l2_get_active_sensor(cam_dev);
> +	if (ret)
> +		return -EPERM;
> +
> +	ret = mtk_isp_config(dev);
> +	if (ret)
> +		return -EPERM;
> +
> +	/* Seninf must stream on first */
> +	dev_dbg(dev, "streamed on: %s\n", cam_dev->seninf->entity.name);
> +	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);
> +	if (ret) {
> +		dev_err(dev, "%s stream on failed:%d\n",
> +			cam_dev->seninf->entity.name, ret);
> +		return -EPERM;
> +	}
> +
> +	dev_dbg(dev, "streamed on: %s\n", cam_dev->sensor->entity.name);
> +	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 1);
> +	if (ret) {
> +		dev_err(dev, "%s stream on failed:%d\n",
> +			cam_dev->sensor->entity.name, ret);
> +		goto fail_sensor_on;
> +	}
> +
> +	cam_dev->streaming = true;
> +	mtk_cam_dev_queue_req_buffers(cam_dev);
> +	isp_composer_stream(isp_ctx, 1);
> +	dev_dbg(dev, "streamed on Pass 1\n");
> +
> +	return 0;
> +
> +fail_sensor_on:
> +	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
> +	return -EPERM;
> +}
> +
> +static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam_dev)
> +{
> +	struct device *dev = &cam_dev->pdev->dev;
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	int ret;
> +
> +	if (!cam_dev->streaming) {
> +		dev_warn(dev, "already stream off");
> +		return 0;
> +	}
> +
> +	dev_dbg(dev, "stream off: %s\n", cam_dev->sensor->entity.name);
> +	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0);
> +	if (ret) {
> +		dev_err(dev, "%s stream off failed:%d\n",
> +			cam_dev->sensor->entity.name, ret);
> +		return -EPERM;
> +	}
> +
> +	dev_dbg(dev, "stream off: %s\n", cam_dev->seninf->entity.name);
> +	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
> +	if (ret) {
> +		dev_err(dev, "%s stream off failed:%d\n",
> +			cam_dev->seninf->entity.name, ret);
> +		goto fail_sensor_off;
> +	}
> +
> +	isp_composer_stream(isp_ctx, 0);
> +	cam_dev->streaming = false;
> +	dev_dbg(dev, "streamed off Pass 1\n");
> +
> +	return 0;
> +
> +fail_sensor_off:
> +	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);

I'd be interested to get Tomasz's input here. If we fail to stream off
one of the subdevs, should we stream on the other one? What if that
fails? It's not clear to me the expectation when stream off fails, but
this seems a bit odd.

> +	return -EPERM;
> +}
> +
> +static int mtk_cam_subdev_s_stream(struct v4l2_subdev *sd,
> +				   int enable)

This can fit on one line.

> +{
> +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> +
> +	if (enable)
> +		return mtk_cam_cio_stream_on(cam_dev);
> +	else
> +		return mtk_cam_cio_stream_off(cam_dev);
> +}
> +
> +static int mtk_cam_subdev_subscribe_event(struct v4l2_subdev *subdev,
> +					  struct v4l2_fh *fh,
> +					  struct v4l2_event_subscription *sub)
> +{
> +	switch (sub->type) {
> +	case V4L2_EVENT_FRAME_SYNC:
> +		return v4l2_event_subscribe(fh, sub, 0, NULL);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int mtk_cam_link_setup(struct media_entity *entity,
> +			      const struct media_pad *local,
> +	const struct media_pad *remote, u32 flags)

Strange indentation here.

> +{
> +	struct mtk_cam_dev *cam_dev =
> +		container_of(entity, struct mtk_cam_dev, subdev.entity);
> +	u32 pad = local->index;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "link setup: %d -> %d\n",
> +		pad, remote->index);
> +
> +	if (pad < cam_dev->dev_node_num)
> +		cam_dev->mem2mem2_nodes[pad].enabled =
> +			!!(flags & MEDIA_LNK_FL_ENABLED);
> +
> +	return 0;
> +}
> +
> +static void mtk_cam_dev_queue_buffers(struct vb2_buffer *vb)
> +{
> +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> +	struct mtk_cam_dev_buffer *buf;
> +
> +	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> +	buf->daddr = vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
> +	buf->scp_addr = 0;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "%pad:%pad\n",
> +		&buf->daddr, &buf->scp_addr);
> +
> +	mtk_isp_enqueue(&cam_dev->pdev->dev, node->desc.dma_port, buf);
> +}
> +
> +static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct mtk_cam_dev *mtk_cam_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> +	struct device *dev = &mtk_cam_dev->pdev->dev;
> +	struct mtk_cam_dev_buffer *buf;
> +	struct vb2_v4l2_buffer *v4l2_buf;
> +
> +	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> +	v4l2_buf = to_vb2_v4l2_buffer(vb);
> +
> +	dev_dbg(dev, "%s: node:%d fd:%d idx:%d\n",
> +		__func__,
> +		node->id,
> +		v4l2_buf->request_fd,
> +		v4l2_buf->vb2_buf.index);
> +
> +	if (v4l2_buf->request_fd < 0) {
> +		mtk_cam_dev_queue_buffers(vb);
> +		return;
> +	}
> +
> +	if (mtk_cam_dev->request_fd != v4l2_buf->request_fd) {
> +		mtk_cam_dev->request_fd = v4l2_buf->request_fd;
> +		mtk_cam_dev->request_count =
> +			vb->req_obj.req->num_incomplete_objects;
> +		dev_dbg(dev, "init  mtk_cam_dev_buf, fd(%d) count(%d)\n",
> +			v4l2_buf->request_fd,
> +			vb->req_obj.req->num_incomplete_objects);
> +	}
> +
> +	/* Added the buffer into the tracking list */
> +	spin_lock(&node->slock);
> +	list_add_tail(&buf->list, &node->pending_list);
> +	spin_unlock(&node->slock);
> +
> +	mtk_cam_dev->request_count--;
> +
> +	if (!mtk_cam_dev->request_count) {
> +		mtk_cam_dev->request_fd = -1;
> +		mtk_cam_dev_queue_req_buffers(mtk_cam_dev);
> +	}
> +}
> +
> +static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
> +				   unsigned int *num_buffers,
> +				   unsigned int *num_planes,
> +				   unsigned int sizes[],
> +				   struct device *alloc_devs[])
> +{
> +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> +	struct device *dev = &cam_dev->pdev->dev;
> +	unsigned int max_buffer_count = node->desc.max_buf_count;
> +	const struct v4l2_format *fmt = &node->vdev_fmt;
> +	unsigned int size;
> +
> +	/* Check the limitation of buffer size */
> +	if (max_buffer_count > 0)
> +		*num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
> +	else
> +		*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
> +
> +	if (node->desc.smem_alloc) {
> +		alloc_devs[0] = cam_dev->smem_dev;
> +		dev_dbg(dev, "Select smem alloc_devs(0x%pK)\n", alloc_devs[0]);
> +	} else {
> +		alloc_devs[0] = &cam_dev->pdev->dev;
> +		dev_dbg(dev, "Select default alloc_devs(0x%pK)\n",
> +			alloc_devs[0]);
> +	}
> +
> +	if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
> +	    vq->type == V4L2_BUF_TYPE_META_CAPTURE)
> +		size = fmt->fmt.meta.buffersize;
> +	else
> +		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
> +
> +	/* Validate initialized num_planes & size[0] */
> +	if (*num_planes) {
> +		if (sizes[0] < size)
> +			return -EINVAL;
> +	} else {
> +		*num_planes = 1;
> +		sizes[0] = size;
> +	}
> +
> +	/* Initialize buffer queue & locks */
> +	INIT_LIST_HEAD(&node->pending_list);
> +	mutex_init(&node->lock);
> +	spin_lock_init(&node->slock);

Aren't these initialized in mtk_cam_mem2mem2_v4l2_register?

> +
> +	return 0;
> +}
> +
> +static bool
> +mtk_cam_all_nodes_streaming(struct mtk_cam_dev *cam_dev,
> +			    struct mtk_cam_video_device *except)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> +		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
> +
> +		if (node == except)
> +			continue;
> +		if (node->enabled && !vb2_start_streaming_called(&node->vbq))
> +			return false;
> +	}
> +
> +	return true;
> +}
> +
> +static void mtk_cam_return_all_buffers(struct mtk_cam_dev *cam_dev,
> +				       struct mtk_cam_video_device *node,
> +				       enum vb2_buffer_state state)
> +{
> +	struct mtk_cam_dev_buffer *b, *b0;
> +	unsigned int i;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "%s: node:%s", __func__, node->vdev.name);
> +
> +	/* Return all buffers */
> +	spin_lock(&node->slock);
> +	list_for_each_entry_safe(b, b0, &node->pending_list, list) {
> +		list_del(&b->list);
> +	}
> +	spin_unlock(&node->slock);
> +
> +	for (i = 0; i < node->vbq.num_buffers; ++i)
> +		if (node->vbq.bufs[i]->state == VB2_BUF_STATE_ACTIVE)
> +			vb2_buffer_done(node->vbq.bufs[i], state);
> +}
> +
> +static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
> +				       unsigned int count)
> +{
> +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> +	int ret;
> +
> +	if (!node->enabled) {
> +		dev_err(&cam_dev->pdev->dev, "Node:%d is not enable\n",
> +			node->id);
> +		ret = -ENOLINK;
> +		goto fail_return_bufs;
> +	}
> +
> +	ret = media_pipeline_start(&node->vdev.entity, &cam_dev->pipeline);
> +	if (ret < 0) {
> +		dev_err(&cam_dev->pdev->dev, "Node:%d %s failed\n",
> +			node->id, __func__);
> +		goto fail_return_bufs;
> +	}
> +
> +	if (!mtk_cam_all_nodes_streaming(cam_dev, node))
> +		return 0;
> +
> +	/* Start streaming of the whole pipeline now */
> +	ret = v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 1);
> +	if (ret < 0) {
> +		dev_err(&cam_dev->pdev->dev, "Node:%d s_stream failed\n",
> +			node->id);
> +		goto fail_stop_pipeline;
> +	}
> +	return 0;
> +
> +fail_stop_pipeline:
> +	media_pipeline_stop(&node->vdev.entity);
> +fail_return_bufs:
> +	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_QUEUED);
> +	return ret;
> +}
> +
> +static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> +
> +	/* Was this the first node with streaming disabled? */
> +	if (mtk_cam_all_nodes_streaming(cam_dev, node)) {
> +		/* Yes, really stop streaming now */
> +		if (v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 0))
> +			dev_err(&cam_dev->pdev->dev,
> +				"failed to stop streaming\n");
> +	}
> +	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_ERROR);
> +	media_pipeline_stop(&node->vdev.entity);
> +}
> +
> +int mtk_cam_videoc_querycap(struct file *file, void *fh,
> +			    struct v4l2_capability *cap)
> +{
> +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> +
> +	strscpy(cap->driver, MTK_CAM_DEV_P1_NAME, sizeof(cap->driver));
> +	strscpy(cap->card, MTK_CAM_DEV_P1_NAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +		 dev_name(cam_dev->media_dev.dev));
> +
> +	return 0;
> +}
> +
> +int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
> +			    struct v4l2_fmtdesc *f)
> +{
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> +	if (f->index >= node->desc.num_fmts || f->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat;
> +	f->flags = 0;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> +	if (f->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	f->fmt = node->vdev_fmt.fmt;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_videoc_try_fmt(struct file *file, void *fh,
> +			   struct v4l2_format *in_fmt)
> +{
> +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +	struct v4l2_format *dev_fmt;
> +	__u32  width, height;
> +
> +	if (in_fmt->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	dev_dbg(&cam_dev->pdev->dev, "%s: fmt:%c%c%c%c, w*h:%u*%u\n",
> +		__func__,
> +		(in_fmt->fmt.pix_mp.pixelformat & 0xFF),
> +		(in_fmt->fmt.pix_mp.pixelformat >> 8) & 0xFF,
> +		(in_fmt->fmt.pix_mp.pixelformat >> 16) & 0xFF,
> +		(in_fmt->fmt.pix_mp.pixelformat >> 24) & 0xFF,
> +		in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height);
> +
> +	width = in_fmt->fmt.pix_mp.width;
> +	height = in_fmt->fmt.pix_mp.height;
> +
> +	dev_fmt = mtk_cam_dev_find_fmt(&node->desc,
> +				       in_fmt->fmt.pix_mp.pixelformat);
> +	if (dev_fmt) {
> +		mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
> +					&in_fmt->fmt.pix_mp,
> +					&dev_fmt->fmt.pix_mp,
> +					node->id);
> +	} else {
> +		mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
> +					     &node->desc,
> +					     in_fmt);
> +	}
> +	in_fmt->fmt.pix_mp.width = clamp_t(u32,
> +					   width,
> +					   CAM_MIN_WIDTH,
> +					   in_fmt->fmt.pix_mp.width);
> +	in_fmt->fmt.pix_mp.height = clamp_t(u32,
> +					    height,
> +					    CAM_MIN_HEIGHT,
> +					    in_fmt->fmt.pix_mp.height);
> +	mtk_cam_dev_cal_mplane_pix_fmt(&cam_dev->pdev->dev,
> +				       &in_fmt->fmt.pix_mp,
> +				       node->id);
> +
> +	return 0;
> +}
> +
> +int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> +	if (f->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	if (cam_dev->streaming)
> +		return -EBUSY;
> +
> +	/* Get the valid format */
> +	mtk_cam_videoc_try_fmt(file, fh, f);
> +
> +	/* Configure to video device */
> +	mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
> +				&node->vdev_fmt.fmt.pix_mp,
> +				&f->fmt.pix_mp,
> +				node->id);
> +
> +	return 0;
> +}
> +
> +int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
> +			      struct v4l2_input *input)
> +{
> +	if (input->index > 0)
> +		return -EINVAL;
> +
> +	strscpy(input->name, "camera", sizeof(input->name));
> +	input->type = V4L2_INPUT_TYPE_CAMERA;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
> +{
> +	*input = 0;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input)
> +{
> +	return input == 0 ? 0 : -EINVAL;
> +}
> +
> +int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
> +				   const struct v4l2_event_subscription *sub)
> +{
> +	switch (sub->type) {
> +	case V4L2_EVENT_CTRL:
> +		return v4l2_ctrl_subscribe_event(fh, sub);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +int mtk_cam_enum_framesizes(struct file *filp, void *priv,
> +			    struct v4l2_frmsizeenum *sizes)
> +{
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
> +	struct v4l2_format *dev_fmt;
> +
> +	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
> +	if (!dev_fmt || sizes->index)
> +		return -EINVAL;
> +
> +	if (node->id == MTK_CAM_P1_MAIN_STREAM_OUT) {
> +		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> +		sizes->stepwise.max_width = IMG_MAX_WIDTH;
> +		sizes->stepwise.min_width = IMG_MIN_WIDTH;
> +		sizes->stepwise.max_height = IMG_MAX_HEIGHT;
> +		sizes->stepwise.min_height = IMG_MIN_HEIGHT;
> +		sizes->stepwise.step_height = 1;
> +		sizes->stepwise.step_width = 1;
> +	} else if (node->id == MTK_CAM_P1_PACKED_BIN_OUT) {
> +		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> +		sizes->stepwise.max_width = RRZ_MAX_WIDTH;
> +		sizes->stepwise.min_width = RRZ_MIN_WIDTH;
> +		sizes->stepwise.max_height = RRZ_MAX_HEIGHT;
> +		sizes->stepwise.min_height = RRZ_MIN_HEIGHT;
> +		sizes->stepwise.step_height = 1;
> +		sizes->stepwise.step_width = 1;
> +	}
> +
> +	return 0;
> +}
> +
> +int mtk_cam_meta_enum_format(struct file *file, void *fh,
> +			     struct v4l2_fmtdesc *f)
> +{
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> +	/* Each node is dedicated to only one meta format */
> +	if (f->index > 0 || f->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	strscpy(f->description, node->desc.description,
> +		sizeof(node->desc.description));
> +	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
> +
> +	return 0;
> +}
> +
> +int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
> +			      struct v4l2_format *f)
> +{
> +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> +	/* Each node is dedicated to only one meta format */
> +	if (f->type != node->vbq.type)
> +		return -EINVAL;
> +
> +	f->fmt = node->vdev_fmt.fmt;
> +
> +	return 0;
> +}
> +
> +/* subdev internal operations */
> +static const struct v4l2_subdev_internal_ops mtk_cam_subdev_internal_ops = {
> +	.open = mtk_cam_subdev_open,
> +	.close = mtk_cam_subdev_close,
> +};
> +
> +static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = {
> +	.subscribe_event = mtk_cam_subdev_subscribe_event,
> +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
> +static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = {
> +	.s_stream = mtk_cam_subdev_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mtk_cam_subdev_ops = {
> +	.core = &mtk_cam_subdev_core_ops,
> +	.video = &mtk_cam_subdev_video_ops,
> +};
> +
> +static const struct media_entity_operations mtk_cam_media_ops = {
> +	.link_setup = mtk_cam_link_setup,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static void mtk_cam_vb2_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct mtk_cam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req,
> +				   dev->v4l2_dev.ctrl_handler);
> +}

Move this function up with the other mtk_cam_vb2_* functions.

> +
> +static const struct vb2_ops mtk_cam_vb2_ops = {
> +	.buf_queue = mtk_cam_vb2_buf_queue,
> +	.queue_setup = mtk_cam_vb2_queue_setup,
> +	.start_streaming = mtk_cam_vb2_start_streaming,
> +	.stop_streaming = mtk_cam_vb2_stop_streaming,
> +	.wait_prepare = vb2_ops_wait_prepare,
> +	.wait_finish = vb2_ops_wait_finish,
> +	.buf_request_complete = mtk_cam_vb2_buf_request_complete,
> +};
> +
> +static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
> +	.unlocked_ioctl = video_ioctl2,
> +	.open = v4l2_fh_open,
> +	.release = vb2_fop_release,
> +	.poll = vb2_fop_poll,
> +	.mmap = vb2_fop_mmap,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl32 = v4l2_compat_ioctl32,
> +#endif
> +};
> +
> +/*
> + * Config node's video properties
> + * according to the device context requirement
> + */
> +static void mtk_cam_node_to_v4l2(struct mtk_cam_dev *cam_dev,
> +				 unsigned int node,
> +				 struct video_device *vdev,
> +				 struct v4l2_format *f)
> +{
> +	struct mtk_cam_dev_node_desc *node_desc =
> +		&cam_dev->mem2mem2_nodes[node].desc;
> +
> +	/* set cap/type/ioctl_ops of the video device */
> +	vdev->device_caps = V4L2_CAP_STREAMING | node_desc->cap;
> +	f->type = node_desc->buf_type;
> +	vdev->ioctl_ops = node_desc->ioctl_ops;
> +
> +	mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
> +				     node_desc,
> +				     f);
> +}
> +
> +static const struct media_device_ops mtk_cam_media_req_ops = {
> +	.req_validate = vb2_request_validate,
> +	.req_queue = vb2_request_queue,
> +};
> +
> +static int mtk_cam_media_register(struct device *dev,
> +				  struct media_device *media_dev)
> +{
> +	int ret;
> +
> +	media_dev->dev = dev;
> +	strscpy(media_dev->model, MTK_CAM_DEV_P1_NAME,
> +		sizeof(media_dev->model));
> +	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
> +		 "platform:%s", dev_name(dev));
> +	media_dev->hw_revision = 0;
> +	media_device_init(media_dev);
> +	media_dev->ops = &mtk_cam_media_req_ops;
> +	dev_info(dev, "Register media device: %s, 0x%pK",
> +		 MTK_CAM_DEV_P1_NAME, media_dev);
> +
> +	ret = media_device_register(media_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register media device (%d)\n", ret);
> +		goto fail_v4l2_dev;
> +	}
> +
> +	return 0;
> +
> +fail_v4l2_dev:
> +	media_device_unregister(media_dev);
> +	media_device_cleanup(media_dev);
> +
> +	return ret;
> +}
> +
> +int mtk_cam_v4l2_register(struct device *dev,
> +			  struct media_device *media_dev,
> +			  struct v4l2_device *v4l2_dev,
> +			  struct v4l2_ctrl_handler *ctrl_handler)

This can be a static function.

> +{
> +	int ret;
> +
> +	/* Set up v4l2 device */
> +	v4l2_dev->ctrl_handler = ctrl_handler;
> +	v4l2_dev->mdev = media_dev;
> +	dev_info(dev, "Register v4l2 device: 0x%pK", v4l2_dev);
> +	ret = v4l2_device_register(dev, v4l2_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
> +		goto fail_v4l2_dev;
> +	}
> +
> +	return 0;
> +
> +fail_v4l2_dev:
> +	media_device_unregister(media_dev);
> +	media_device_cleanup(media_dev);

The calling function will do this cleanup when this function fails, so
no need to do it here; just return ret.

> +
> +	return ret;
> +}
> +
> +int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev)
> +{
> +	struct device *dev = &cam_dev->pdev->dev;
> +	unsigned int num_nodes = cam_dev->dev_node_num;
> +	/* Total pad numbers is video devices + one seninf pad */
> +	unsigned int num_subdev_pads = MTK_CAM_DEV_NODES + 1;
> +	unsigned int i;
> +	int ret;
> +
> +	ret = mtk_cam_media_register(dev,
> +				     &cam_dev->media_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register media device:%d\n", ret);
> +		goto fail_media_dev;
> +	}
> +
> +	ret = mtk_cam_v4l2_register(dev,
> +				    &cam_dev->media_dev,
> +				    &cam_dev->v4l2_dev,
> +				    NULL);
> +	if (ret) {
> +		dev_err(dev, "failed to register V4L2 device:%d\n", ret);
> +		goto fail_v4l2_dev;
> +	}
> +
> +	/* Initialize subdev media entity */
> +	cam_dev->subdev_pads = devm_kcalloc(dev, num_subdev_pads,
> +					    sizeof(*cam_dev->subdev_pads),
> +					    GFP_KERNEL);

It doesn't look like this gets free'd in any of the failure cases below.

> +	if (!cam_dev->subdev_pads) {
> +		ret = -ENOMEM;
> +		goto fail_subdev_pads;
> +	}
> +
> +	ret = media_entity_pads_init(&cam_dev->subdev.entity,
> +				     num_subdev_pads,
> +				     cam_dev->subdev_pads);
> +	if (ret) {
> +		dev_err(dev, "failed initialize media pads:%d:\n", ret);
> +		goto fail_subdev_pads;
> +	}
> +
> +	/* Initialize all pads with MEDIA_PAD_FL_SOURCE */
> +	for (i = 0; i < num_subdev_pads; i++)
> +		cam_dev->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Customize the last one pad as CIO sink pad. */
> +	cam_dev->subdev_pads[MTK_CAM_DEV_NODES].flags = MEDIA_PAD_FL_SINK;
> +
> +	/* Initialize subdev */
> +	v4l2_subdev_init(&cam_dev->subdev, &mtk_cam_subdev_ops);
> +	cam_dev->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
> +	cam_dev->subdev.entity.ops = &mtk_cam_media_ops;
> +	cam_dev->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
> +				V4L2_SUBDEV_FL_HAS_EVENTS;
> +	snprintf(cam_dev->subdev.name, sizeof(cam_dev->subdev.name),
> +		 "%s", MTK_CAM_DEV_P1_NAME);
> +	v4l2_set_subdevdata(&cam_dev->subdev, cam_dev);
> +	cam_dev->subdev.internal_ops = &mtk_cam_subdev_internal_ops;
> +
> +	dev_info(dev, "register subdev: %s\n", cam_dev->subdev.name);
> +	ret = v4l2_device_register_subdev(&cam_dev->v4l2_dev, &cam_dev->subdev);
> +	if (ret) {
> +		dev_err(dev, "failed initialize subdev:%d\n", ret);
> +		goto fail_subdev;
> +	}
> +
> +	/* Create video nodes and links */
> +	for (i = 0; i < num_nodes; i++) {

Consider moving some of this loop to a new function. This would simplify
the failure handling below by by removing the fail_vdev and
fail_vdev_media_entity labels, whose cleanup could move into the helper
function. It would also break up this large function a bit.

> +		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
> +		struct video_device *vdev = &node->vdev;
> +		struct vb2_queue *vbq = &node->vbq;
> +		u32 output = !cam_dev->mem2mem2_nodes[i].desc.capture;
> +		u32 link_flags = cam_dev->mem2mem2_nodes[i].desc.link_flags;
> +
> +		cam_dev->subdev_pads[i].flags = output ?
> +			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
> +
> +		/* Initialize miscellaneous variables */
> +		mutex_init(&node->lock);
> +		spin_lock_init(&node->slock);
> +		INIT_LIST_HEAD(&node->pending_list);
> +
> +		/* Initialize formats to default values */
> +		mtk_cam_node_to_v4l2(cam_dev, i, vdev, &node->vdev_fmt);
> +
> +		/* Initialize media entities */
> +		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
> +		if (ret) {
> +			dev_err(dev, "failed initialize media pad:%d\n", ret);
> +			goto fail_vdev_media_entity;
> +		}
> +		node->enabled = false;
> +		node->id = i;
> +		node->vdev_pad.flags = cam_dev->subdev_pads[i].flags;
> +		vdev->entity.ops = NULL;
> +
> +		/* Initialize vbq */
> +		vbq->type = node->vdev_fmt.type;
> +		if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT)
> +			vbq->io_modes = VB2_MMAP;
> +		else
> +			vbq->io_modes = VB2_MMAP | VB2_DMABUF;
> +		if (node->desc.smem_alloc)
> +			vbq->bidirectional = 1;
> +		if (vbq->type == V4L2_BUF_TYPE_META_CAPTURE)
> +			vdev->entity.function =
> +				MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
> +		vbq->ops = &mtk_cam_vb2_ops;
> +		vbq->mem_ops = &vb2_dma_contig_memops;
> +		vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer);
> +		vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +		vbq->min_buffers_needed = 0;	/* Can streamon w/o buffers */
> +		/* Put the process hub sub device in the vb2 private data */
> +		vbq->drv_priv = cam_dev;
> +		vbq->lock = &node->lock;
> +		vbq->supports_requests = true;
> +
> +		ret = vb2_queue_init(vbq);
> +		if (ret) {
> +			dev_err(dev, "failed to init. vb2 queue:%d\n", ret);
> +			goto fail_vdev;
> +		}
> +
> +		/* Initialize vdev */
> +		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
> +			 MTK_CAM_DEV_P1_NAME, node->desc.name);
> +		vdev->release = video_device_release_empty;
> +		vdev->fops = &mtk_cam_v4l2_fops;
> +		vdev->lock = &node->lock;
> +		vdev->v4l2_dev = &cam_dev->v4l2_dev;
> +		vdev->queue = &node->vbq;
> +		vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
> +		/* Enable private control for image video devices */
> +		if (node->desc.image) {
> +			mtk_cam_ctrl_init(cam_dev, &node->ctrl_handler);
> +			vdev->ctrl_handler = &node->ctrl_handler;
> +		}
> +		video_set_drvdata(vdev, cam_dev);
> +		dev_dbg(dev, "register vdev:%d:%s\n", i, vdev->name);
> +
> +		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +		if (ret) {
> +			dev_err(dev, "failed to register vde:%d\n", ret);
> +			goto fail_vdev;
> +		}
> +
> +		/* Create link between video node and the subdev pad */
> +		if (output) {
> +			ret = media_create_pad_link(&vdev->entity, 0,
> +						    &cam_dev->subdev.entity,
> +						    i, link_flags);
> +		} else {
> +			ret = media_create_pad_link(&cam_dev->subdev.entity,
> +						    i, &vdev->entity, 0,
> +						    link_flags);
> +		}
> +		if (ret)
> +			goto fail_link;
> +	}
> +
> +	vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
> +
> +	return 0;
> +
> +	for (; i >= 0; i--) {
> +fail_link:
> +		video_unregister_device(&cam_dev->mem2mem2_nodes[i].vdev);
> +fail_vdev:
> +		media_entity_cleanup(&cam_dev->mem2mem2_nodes[i].vdev.entity);
> +fail_vdev_media_entity:
> +		mutex_destroy(&cam_dev->mem2mem2_nodes[i].lock);
> +	}
> +fail_subdev:
> +	media_entity_cleanup(&cam_dev->subdev.entity);
> +fail_subdev_pads:
> +	v4l2_device_unregister(&cam_dev->v4l2_dev);
> +fail_v4l2_dev:
> +fail_media_dev:
>
media_device_unregister and media_device_cleanup are called when
mtk_cam_media_register fails, so only need to call these under the
fail_v4l2_dev label.

> +	dev_err(dev, "fail_v4l2_dev mdev: 0x%pK:%d", &cam_dev->media_dev, ret);
> +	media_device_unregister(&cam_dev->media_dev);
> +	media_device_cleanup(&cam_dev->media_dev);
> +
> +	return ret;
> +}
> +
> +int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev)
> +{
> +	unsigned int i;
> +	struct mtk_cam_video_device *dev;
> +
> +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> +		dev = &cam_dev->mem2mem2_nodes[i];
> +		video_unregister_device(&dev->vdev);
> +		media_entity_cleanup(&dev->vdev.entity);
> +		mutex_destroy(&dev->lock);
> +		if (dev->desc.image)
> +			v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +	}
> +
> +	vb2_dma_contig_clear_max_seg_size(&cam_dev->pdev->dev);
> +	v4l2_device_unregister_subdev(&cam_dev->subdev);
> +	media_entity_cleanup(&cam_dev->subdev.entity);
> +	kfree(cam_dev->subdev_pads);
> +	v4l2_device_unregister(&cam_dev->v4l2_dev);
> +	media_device_unregister(&cam_dev->media_dev);
> +	media_device_cleanup(&cam_dev->media_dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_cam_dev_complete(struct v4l2_async_notifier *notifier)
> +{
> +	struct mtk_cam_dev *cam_dev =
> +		container_of(notifier, struct mtk_cam_dev, notifier);
> +	struct device *dev = &cam_dev->pdev->dev;
> +	int ret;
> +
> +	ret = media_create_pad_link(&cam_dev->seninf->entity,
> +				    MTK_CAM_SENINF_PAD_SRC,
> +				    &cam_dev->subdev.entity,
> +				    MTK_CAM_P1_HUB_PAD_SINK,
> +				    0);
> +	if (ret)

should this function return an error here?

> +		dev_err(dev, "fail to create pad link %s %s err:%d\n",
> +			cam_dev->seninf->entity.name,
> +			cam_dev->subdev.entity.name,
> +			ret);
> +
> +	dev_info(dev, "Complete the v4l2 registration\n");
> +
> +	ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev);
> +	if (ret) {
> +		dev_err(dev, "failed initialize subdev nodes:%d\n", ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier,
> +				      struct v4l2_subdev *sd,
> +				      struct v4l2_async_subdev *asd)
> +{
> +	struct mtk_cam_dev *cam_dev =
> +		container_of(notifier, struct mtk_cam_dev, notifier);
> +
> +	cam_dev->seninf = sd;
> +	dev_info(&cam_dev->pdev->dev, "%s is bounded\n", sd->entity.name);
> +	return 0;
> +}
> +
> +static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier,
> +					struct v4l2_subdev *sd,
> +					struct v4l2_async_subdev *asd)
> +{
> +	struct mtk_cam_dev *cam_dev =
> +		container_of(notifier, struct mtk_cam_dev, notifier);

Should anything be done to cam_dev-seninf here, such as setting it to
NULL?

> +
> +	dev_dbg(&cam_dev->pdev->dev, "%s is unbounded\n", sd->entity.name);
> +}
> +
> +static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier)
> +{
> +	return mtk_cam_dev_complete(notifier);
> +}
> +
> +static const struct v4l2_async_notifier_operations mtk_cam_async_ops = {
> +	.bound = mtk_cam_dev_notifier_bound,
> +	.unbind = mtk_cam_dev_notifier_unbind,
> +	.complete = mtk_cam_dev_notifier_complete,
> +};
> +
> +static int mtk_cam_dev_fwnode_parse(struct device *dev,
> +				    struct v4l2_fwnode_endpoint *vep,
> +				    struct v4l2_async_subdev *asd)
> +{
> +	return 0;
> +}
> +
> +int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev)
> +{
> +	int ret;
> +
> +	ret = v4l2_async_notifier_parse_fwnode_endpoints(
> +		&cam_dev->pdev->dev, &cam_dev->notifier,
> +		sizeof(struct v4l2_async_subdev),
> +		mtk_cam_dev_fwnode_parse);

As far as I can tell, the fourth argument is optional, so I think you
can just set NULL and remove mtk_cam_dev_fwnode_parse.

> +	if (ret < 0)
> +		return ret;
> +
> +	if (!cam_dev->notifier.num_subdevs)
> +		return -ENODEV;
> +
> +	cam_dev->notifier.ops = &mtk_cam_async_ops;
> +	dev_info(&cam_dev->pdev->dev, "mtk_cam v4l2_async_notifier_register\n");
> +	ret = v4l2_async_notifier_register(&cam_dev->v4l2_dev,
> +					   &cam_dev->notifier);
> +	if (ret) {
> +		dev_err(&cam_dev->pdev->dev,
> +			"failed to register async notifier : %d\n", ret);
> +		v4l2_async_notifier_cleanup(&cam_dev->notifier);
> +	}
> +
> +	return ret;
> +}
> +
> +void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev)
> +{
> +	v4l2_async_notifier_unregister(&cam_dev->notifier);
> +	v4l2_async_notifier_cleanup(&cam_dev->notifier);
> +}
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
> new file mode 100644
> index 000000000000..73b36916da08
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Frederic Chen <frederic.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MTK_CAM_DEV_V4L2_H__
> +#define __MTK_CAM_DEV_V4L2_H__
> +
> +#include <media/v4l2-device.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +int mtk_cam_videoc_querycap(struct file *file, void *fh,
> +			    struct v4l2_capability *cap);
> +int mtk_cam_enum_framesizes(struct file *filp, void *priv,
> +			    struct v4l2_frmsizeenum *sizes);
> +int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
> +			    struct v4l2_fmtdesc *f);
> +int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
> +int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
> +int mtk_cam_videoc_try_fmt(struct file *file,
> +			   void *fh, struct v4l2_format *in_fmt);
> +int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
> +			      struct v4l2_input *input);
> +int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input);
> +int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input);
> +int mtk_cam_meta_enum_format(struct file *file, void *fh,
> +			     struct v4l2_fmtdesc *f);
> +int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
> +			      struct v4l2_format *f);
> +int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
> +				   const struct v4l2_event_subscription *sub);
> +
> +#endif /* __MTK_CAM_DEV_V4L2_H__ */
> -- 
> 2.18.0
> 

_______________________________________________
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] 104+ messages in thread

* Re: [RFC, V2, 09/11] media: platform: Add Mediatek ISP P1 device driver
  2019-05-10  1:58 ` [RFC,V2,09/11] media: platform: Add Mediatek ISP P1 device driver Jungo Lin
@ 2019-05-24 21:19   ` " Drew Davenport
  2019-05-27 13:07     ` Jungo Lin
  0 siblings, 1 reply; 104+ messages in thread
From: Drew Davenport @ 2019-05-24 21:19 UTC (permalink / raw)
  To: Jungo Lin
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

Hi Jungo,

On Fri, May 10, 2019 at 09:58:04AM +0800, Jungo Lin wrote:
> This patch adds the Mediatek ISP P1 HW control device driver.
> It handles the ISP HW configuration, provides interrupt handling and
> initializes the V4L2 device nodes and other functions.

A few comments inline.

> 
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> ---
>  .../mtk-isp/isp_50/cam/mtk_cam-regs.h         |  149 ++
>  .../platform/mtk-isp/isp_50/cam/mtk_cam.c     | 1206 +++++++++++++++++
>  .../platform/mtk-isp/isp_50/cam/mtk_cam.h     |  300 ++++
>  3 files changed, 1655 insertions(+)
>  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
>  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
>  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> 
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> new file mode 100644
> index 000000000000..342f0e0e9837
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> @@ -0,0 +1,149 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ryan Yu <ryan.yu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _CAM_REGS_H
> +#define _CAM_REGS_H
> +
> +/* TG Bit Mask */
> +#define VFDATA_EN_BIT	BIT(0)
> +#define CMOS_EN_BIT	BIT(0)
> +
> +/* normal signal bit */
> +#define VS_INT_ST	BIT(0)
> +#define HW_PASS1_DON_ST	BIT(11)
> +#define SOF_INT_ST	BIT(12)
> +#define SW_PASS1_DON_ST	BIT(30)
> +
> +/* err status bit */
> +#define TG_ERR_ST	BIT(4)
> +#define TG_GBERR_ST	BIT(5)
> +#define CQ_CODE_ERR_ST	BIT(6)
> +#define CQ_APB_ERR_ST	BIT(7)
> +#define CQ_VS_ERR_ST	BIT(8)
> +#define AMX_ERR_ST	BIT(15)
> +#define RMX_ERR_ST	BIT(16)
> +#define BMX_ERR_ST	BIT(17)
> +#define RRZO_ERR_ST	BIT(18)
> +#define AFO_ERR_ST	BIT(19)
> +#define IMGO_ERR_ST	BIT(20)
> +#define AAO_ERR_ST	BIT(21)
> +#define PSO_ERR_ST	BIT(22)
> +#define LCSO_ERR_ST	BIT(23)
> +#define BNR_ERR_ST	BIT(24)
> +#define LSCI_ERR_ST	BIT(25)
> +#define DMA_ERR_ST	BIT(29)
> +
> +/* CAM DMA done status */
> +#define FLKO_DONE_ST	BIT(4)
> +#define AFO_DONE_ST	BIT(5)
> +#define AAO_DONE_ST	BIT(7)
> +#define PSO_DONE_ST	BIT(14)

Please align the values using tabs here and elsewhere.

> +
> +/* IRQ signal mask */
> +#define INT_ST_MASK_CAM	( \
> +			VS_INT_ST |\
> +			SOF_INT_ST |\
> +			HW_PASS1_DON_ST |\
> +			SW_PASS1_DON_ST)
> +
> +/* IRQ Warning Mask */
> +#define INT_ST_MASK_CAM_WARN	(\
> +				RRZO_ERR_ST |\
> +				AFO_ERR_ST |\
> +				IMGO_ERR_ST |\
> +				AAO_ERR_ST |\
> +				PSO_ERR_ST | \
> +				LCSO_ERR_ST |\
> +				BNR_ERR_ST |\
> +				LSCI_ERR_ST)
> +
> +/* IRQ Error Mask */
> +#define INT_ST_MASK_CAM_ERR	(\
> +				TG_ERR_ST |\
> +				TG_GBERR_ST |\
> +				CQ_CODE_ERR_ST |\
> +				CQ_APB_ERR_ST |\
> +				CQ_VS_ERR_ST |\
> +				BNR_ERR_ST |\
> +				RMX_ERR_ST |\
> +				BMX_ERR_ST |\
> +				BNR_ERR_ST |\
> +				LSCI_ERR_ST |\
> +				DMA_ERR_ST)
> +
> +/* IRQ Signal Log Mask */
> +#define INT_ST_LOG_MASK_CAM	(\
> +				SOF_INT_ST |\
> +				SW_PASS1_DON_ST |\
> +				VS_INT_ST |\
> +				TG_ERR_ST |\
> +				TG_GBERR_ST |\
> +				RRZO_ERR_ST |\
> +				AFO_ERR_ST |\
> +				IMGO_ERR_ST |\
> +				AAO_ERR_ST |\
> +				DMA_ERR_ST)
> +
> +/* DMA Event Notification Mask */
> +#define DMA_ST_MASK_CAM	(\
> +			AFO_DONE_ST |\
> +			AAO_DONE_ST |\
> +			PSO_DONE_ST |\
> +			FLKO_DONE_ST)
> +
> +/* Status check */
> +#define REG_CTL_EN		0x0004
> +#define REG_CTL_DMA_EN		0x0008
> +#define REG_CTL_FMT_SEL		0x0010
> +#define REG_CTL_EN2		0x0018
> +#define REG_CTL_RAW_INT_EN	0x0020
> +#define REG_CTL_RAW_INT_STAT	0x0024
> +#define REG_CTL_RAW_INT2_STAT	0x0034
> +#define REG_CTL_RAW_INT3_STAT	0x00c4
> +#define REG_CTL_TWIN_STAT	0x0050
> +
> +#define REG_TG_SEN_MODE		0x0230
> +#define REG_TG_SEN_GRAB_PIX	0x0238
> +#define REG_TG_SEN_GRAB_LIN	0x023c
> +#define REG_TG_VF_CON		0x0234
> +#define REG_TG_SUB_PERIOD	0x02a4
> +
> +#define REG_IMGO_BASE_ADDR	0x1020
> +#define REG_RRZO_BASE_ADDR	0x1050
> +
> +/* Error status log */
> +#define REG_IMGO_ERR_STAT	0x1360
> +#define REG_RRZO_ERR_STAT	0x1364
> +#define REG_AAO_ERR_STAT	0x1368
> +#define REG_AFO_ERR_STAT	0x136c
> +#define REG_LCSO_ERR_STAT	0x1370
> +#define REG_UFEO_ERR_STAT	0x1374
> +#define REG_PDO_ERR_STAT	0x1378
> +#define REG_BPCI_ERR_STAT	0x137c
> +#define REG_LSCI_ERR_STAT	0x1384
> +#define REG_PDI_ERR_STAT	0x138c
> +#define REG_LMVO_ERR_STAT	0x1390
> +#define REG_FLKO_ERR_STAT	0x1394
> +#define REG_PSO_ERR_STAT	0x13a0
> +
> +/* ISP command */
> +#define REG_CQ_THR0_BASEADDR	0x0198
> +#define REG_HW_FRAME_NUM	0x13b8
> +
> +/* META */
> +#define REG_META0_VB2_INDEX	0x14dc
> +#define REG_META1_VB2_INDEX	0x151c
> +
> +#endif	/* _CAM_REGS_H */
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> new file mode 100644
> index 000000000000..fc874ec8f7f0
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> @@ -0,0 +1,1206 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ryan Yu <ryan.yu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/atomic.h>
> +#include <linux/cdev.h>
> +#include <linux/compat.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/ktime.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/mtk_scp.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/remoteproc.h>
> +#include <linux/sched/clock.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/vmalloc.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-regs.h"
> +#include "mtk_cam-smem.h"
> +
> +static const struct of_device_id mtk_isp_of_ids[] = {
> +	{.compatible = "mediatek,mt8183-camisp",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_isp_of_ids);
> +
> +/* list of clocks required by isp cam */
> +static const char * const mtk_isp_clks[] = {
> +	"CAMSYS_CAM_CGPDN", "CAMSYS_CAMTG_CGPDN"
> +};
> +
> +static void isp_dump_dma_status(struct isp_device *isp_dev)
> +{
> +	dev_err(isp_dev->dev,
> +		"IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n",
> +		readl(isp_dev->regs + REG_IMGO_ERR_STAT),
> +		readl(isp_dev->regs + REG_RRZO_ERR_STAT),
> +		readl(isp_dev->regs + REG_AAO_ERR_STAT),
> +		readl(isp_dev->regs + REG_AFO_ERR_STAT),
> +		readl(isp_dev->regs + REG_LMVO_ERR_STAT));
> +	dev_err(isp_dev->dev,
> +		"LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n",
> +		readl(isp_dev->regs + REG_LCSO_ERR_STAT),
> +		readl(isp_dev->regs + REG_PSO_ERR_STAT),
> +		readl(isp_dev->regs + REG_FLKO_ERR_STAT),
> +		readl(isp_dev->regs + REG_BPCI_ERR_STAT),
> +		readl(isp_dev->regs + REG_LSCI_ERR_STAT));
> +}
> +
> +static void mtk_isp_notify(struct mtk_isp_p1_ctx *isp_ctx,
> +			   unsigned int request_fd,
> +			   unsigned int frame_seq_no,
> +			   struct list_head *list_buf,
> +			   enum vb2_buffer_state state)
> +{
> +	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
> +	struct device *dev = &p1_dev->pdev->dev;
> +	struct mtk_cam_dev_finish_param fram_param;
> +
> +	fram_param.list_buf = list_buf;
> +	fram_param.request_fd = request_fd;
> +	fram_param.frame_seq_no = frame_seq_no;
> +	fram_param.state = state;
> +	dev_dbg(dev, "request fd:%d frame_seq_no:%d\n",
> +		fram_param.request_fd,
> +		fram_param.frame_seq_no);
> +	mtk_cam_dev_job_finish(p1_dev->cam_dev, &fram_param);
> +}
> +
> +static void isp_deque_frame(struct isp_p1_device *p1_dev,
> +			    unsigned int node_id, int vb2_index,
> +			    int frame_seq_no)
> +{
> +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> +	struct device *dev = &p1_dev->pdev->dev;
> +	struct vb2_queue *vb2_queue = &cam_dev->mem2mem2_nodes[node_id].vbq;
> +	struct vb2_buffer *vb;
> +	struct vb2_v4l2_buffer *vbb;
> +
> +	if (!cam_dev->mem2mem2_nodes[node_id].enabled)
> +		return;
> +
> +	mutex_lock(vb2_queue->lock);
> +	list_for_each_entry(vb, &vb2_queue->queued_list, queued_entry) {
> +		vbb = to_vb2_v4l2_buffer(vb);
> +		if (vbb->request_fd < 0 &&
> +		    vb->index == vb2_index &&
> +		    vb->state == VB2_BUF_STATE_ACTIVE) {
> +			dev_dbg(dev, "%s:%d:%d", __func__, node_id, vb2_index);
> +			vbb->vb2_buf.timestamp = ktime_get_ns();
> +			vbb->sequence = frame_seq_no;
> +			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
> +		}
> +	}
> +	mutex_unlock(vb2_queue->lock);
> +}
> +
> +static void isp_deque_request_frame(struct isp_p1_device *p1_dev,
> +				    int frame_seq_no)
> +{
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct device *dev = &p1_dev->pdev->dev;
> +	struct mtk_isp_queue_job *framejob, *tmp;
> +	struct isp_queue *p1_enqueue_list = &isp_ctx->p1_enqueue_list;
> +
> +	/* Match dequeue work and enqueue frame */
> +	spin_lock(&p1_enqueue_list->lock);
> +	list_for_each_entry_safe(framejob, tmp, &p1_enqueue_list->queue,
> +				 list_entry) {
> +		dev_dbg(dev,
> +			"%s frame_seq_no:%d, target frame_seq_no:%d\n",
> +			__func__,
> +			framejob->frame_seq_no, frame_seq_no);
> +		/* Match by the en-queued request number */
> +		if (framejob->frame_seq_no == frame_seq_no) {
> +			/* Pass to user space */
> +			mtk_isp_notify(isp_ctx,
> +				       framejob->request_fd,
> +				       framejob->frame_seq_no,
> +				       &framejob->list_buf,
> +				       VB2_BUF_STATE_DONE);
> +			atomic_dec(&p1_enqueue_list->queue_cnt);
> +			dev_dbg(dev,
> +				"frame_seq_no:%d is done, queue_cnt:%d\n",
> +				framejob->frame_seq_no,
> +				atomic_read(&p1_enqueue_list->queue_cnt));
> +
> +			/* remove only when frame ready */
> +			list_del(&framejob->list_entry);
> +			kfree(framejob);
> +			break;
> +		} else if (framejob->frame_seq_no < frame_seq_no) {
> +			/* Pass to user space for frame drop */
> +			mtk_isp_notify(isp_ctx,
> +				       framejob->request_fd,
> +				       framejob->frame_seq_no,
> +				       &framejob->list_buf,
> +				       VB2_BUF_STATE_ERROR);
> +			atomic_dec(&p1_enqueue_list->queue_cnt);
> +			dev_dbg(dev,
> +				"frame_seq_no:%d drop, queue_cnt:%d\n",
> +				framejob->frame_seq_no,
> +				atomic_read(&p1_enqueue_list->queue_cnt));
> +
> +			/* remove only drop frame */
> +			list_del(&framejob->list_entry);
> +			kfree(framejob);
> +		}
> +	}
> +	spin_unlock(&p1_enqueue_list->lock);
> +}
> +
> +static int isp_deque_work(void *data)
> +{
> +	struct isp_p1_device *p1_dev = (struct isp_p1_device *)data;
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct device *dev = &p1_dev->pdev->dev;
> +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> +	struct mtk_cam_dev_stat_event_data event_data;
> +	atomic_t *irq_data_end = &isp_ctx->irq_data_end;
> +	atomic_t *irq_data_start = &isp_ctx->irq_data_start;
> +	unsigned long flags;
> +	int ret, i;
> +
> +	while (1) {
> +		ret = wait_event_interruptible(isp_ctx->isp_deque_thread.wq,
> +					       (atomic_read(irq_data_end) !=
> +					       atomic_read(irq_data_start)) ||
> +					       kthread_should_stop());
> +
> +		if (kthread_should_stop())
> +			break;
> +
> +		if (ret == ERESTARTSYS) {
> +			dev_err(dev, "interrupted by a signal!\n");
> +			continue;
> +		}
> +
> +		spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
> +		i = atomic_read(&isp_ctx->irq_data_start);
> +		memcpy(&event_data, &isp_ctx->irq_event_datas[i],
> +		       sizeof(event_data));
> +		memset(&isp_ctx->irq_event_datas[i], 0x00, sizeof(event_data));
> +		atomic_set(&isp_ctx->irq_data_start, ++i & 0x3);
> +		spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
> +
> +		if (event_data.irq_status_mask & VS_INT_ST) {
> +			/* Notify specific HW events to user space */
> +			mtk_cam_dev_event_frame_sync(cam_dev,
> +						     event_data.frame_seq_no);
> +			dev_dbg(dev,
> +				"event IRQ:0x%x DMA:0x%x is sent\n",
> +				event_data.irq_status_mask,
> +				event_data.dma_status_mask);
> +		}
> +
> +		if (event_data.dma_status_mask & AAO_DONE_ST) {
> +			isp_deque_frame(p1_dev,
> +					MTK_CAM_P1_META_OUT_0,
> +					event_data.meta0_vb2_index,
> +					event_data.frame_seq_no);
> +		}
> +
> +		if (event_data.irq_status_mask & SW_PASS1_DON_ST) {
> +			isp_deque_frame(p1_dev,
> +					MTK_CAM_P1_META_OUT_0,
> +					event_data.meta0_vb2_index,
> +					event_data.frame_seq_no);
> +
> +			isp_deque_request_frame(p1_dev,
> +						event_data.frame_seq_no);
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int irq_handle_sof(struct isp_device *isp_dev,
> +			  dma_addr_t base_addr,
> +			  unsigned int frame_num)
> +{
> +	unsigned int cq_addr_index;
> +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> +	int cq_num = atomic_read(&p1_dev->isp_ctx.composed_frame_id);
> +
> +	if (cq_num > frame_num) {
> +		cq_addr_index = frame_num % CQ_BUFFER_COUNT;
> +
> +		writel(base_addr +
> +			(dma_addr_t)(CQ_ADDRESS_OFFSET * cq_addr_index),
> +			isp_dev->regs + REG_CQ_THR0_BASEADDR);
> +		dev_dbg(isp_dev->dev,
> +			"SOF_INT_ST, update next, cq_num:%d, frame_num:%d cq_addr:%d",
> +			cq_num, frame_num, cq_addr_index);
> +	} else {
> +		dev_dbg(isp_dev->dev,
> +			"SOF_INT_ST, wait next, cq_num:%d, frame_num:%d",
> +			cq_num, frame_num);
> +	}
> +
> +	isp_dev->sof_count += 1;
> +
> +	return cq_num;
> +}
> +
> +static int irq_handle_notify_event(struct isp_device *isp_dev,
> +				   unsigned int irqstatus,
> +				   unsigned int dmastatus)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	unsigned long flags;
> +	int i;
> +
> +	spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
> +	i = atomic_read(&isp_ctx->irq_data_end);
> +	isp_ctx->irq_event_datas[i].frame_seq_no = isp_dev->current_frame;
> +	isp_ctx->irq_event_datas[i].meta0_vb2_index = isp_dev->meta0_vb2_index;
> +	isp_ctx->irq_event_datas[i].meta1_vb2_index = isp_dev->meta1_vb2_index;
> +	isp_ctx->irq_event_datas[i].irq_status_mask |=
> +		(irqstatus & INT_ST_MASK_CAM);
> +	isp_ctx->irq_event_datas[i].dma_status_mask |=
> +		(dmastatus & DMA_ST_MASK_CAM);
> +	atomic_set(&isp_ctx->irq_data_end, ++i & 0x3);
> +	spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
> +
> +	wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
> +
> +	dev_dbg(isp_dev->dev,
> +		"%s IRQ:0x%x DMA:0x%x seq:%d idx0:%d idx1:%d\n",
> +		__func__,
> +		(irqstatus & INT_ST_MASK_CAM),
> +		(dmastatus & DMA_ST_MASK_CAM),
> +		isp_dev->current_frame,
> +		isp_dev->meta0_vb2_index,
> +		isp_dev->meta1_vb2_index);
> +
> +	return 0;
> +}
> +
> +irqreturn_t isp_irq_cam(int irq, void *data)
> +{
> +	struct isp_device *isp_dev = (struct isp_device *)data;
> +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct device *dev = isp_dev->dev;
> +	unsigned int cardinalnum, cq_num, hw_frame_num;
> +	unsigned int meta0_vb2_index, meta1_vb2_index;
> +	unsigned int irqstatus, errstatus, warnstatus, dmastatus;
> +	unsigned long flags;
> +
> +	/* Check the streaming is off or not */
> +	if (!p1_dev->cam_dev->streaming)
> +		return IRQ_HANDLED;
> +
> +	cardinalnum = isp_dev->isp_hw_module - ISP_CAM_A_IDX;
> +	cq_num = 0;
> +
> +	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
> +	irqstatus = readl(isp_dev->regs + REG_CTL_RAW_INT_STAT);
> +	dmastatus = readl(isp_dev->regs + REG_CTL_RAW_INT2_STAT);
> +	hw_frame_num = readl(isp_dev->regs + REG_HW_FRAME_NUM);
> +	meta0_vb2_index = readl(isp_dev->regs + REG_META0_VB2_INDEX);
> +	meta1_vb2_index = readl(isp_dev->regs + REG_META1_VB2_INDEX);
> +	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
> +
> +	/* Ignore unnecessary IRQ */
> +	if (irqstatus == 0)
> +		return IRQ_HANDLED;
> +
> +	errstatus = irqstatus & INT_ST_MASK_CAM_ERR;
> +	warnstatus = irqstatus & INT_ST_MASK_CAM_WARN;
> +	irqstatus = irqstatus & INT_ST_MASK_CAM;
> +
> +	/* sof , done order check . */
> +	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
> +	if ((irqstatus & HW_PASS1_DON_ST) && (irqstatus & SOF_INT_ST)) {
> +		dev_warn(dev,
> +			 "isp sof_don block, sof_cnt:%d\n",
> +			 isp_dev->sof_count);
> +
> +		/* Notify IRQ event and enqueue ready frame */
> +		irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
> +		isp_dev->current_frame = hw_frame_num;
> +		isp_dev->meta0_vb2_index = meta0_vb2_index;
> +		isp_dev->meta1_vb2_index = meta1_vb2_index;
> +	} else {
> +		if (irqstatus & SOF_INT_ST) {
> +			isp_dev->current_frame = hw_frame_num;
> +			isp_dev->meta0_vb2_index = meta0_vb2_index;
> +			isp_dev->meta1_vb2_index = meta1_vb2_index;
> +		}
> +
> +		if ((irqstatus & INT_ST_MASK_CAM) ||
> +		    (dmastatus & DMA_ST_MASK_CAM))
> +			irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
> +	}
> +	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
> +
> +	if (irqstatus & SOF_INT_ST)
> +		cq_num = irq_handle_sof(isp_dev, isp_ctx->scp_mem_iova,
> +					hw_frame_num);
> +
> +	if (irqstatus & SW_PASS1_DON_ST) {
> +		int num = atomic_dec_return(&isp_ctx->composing_frame);
> +
> +		dev_dbg(dev, "SW_PASS1_DON_ST queued frame:%d\n", num);
> +		/* Notify TX thread to send if TX frame is blocked */
> +		wake_up_interruptible
> +				(&isp_ctx->composer_tx_thread.wq);
> +	}
> +
> +	/* check ISP error status */
> +	if (errstatus) {
> +		dev_err(dev,
> +			"raw_int_err:0x%x/0x%x/0x%x\n",
> +			irqstatus, warnstatus, errstatus);
> +
> +		/* show DMA errors in detail */
> +		if (errstatus & DMA_ERR_ST)
> +			isp_dump_dma_status(isp_dev);
> +	}
> +
> +	if (irqstatus & INT_ST_LOG_MASK_CAM)
> +		dev_dbg(dev, IRQ_STAT_STR,
> +			'A' + cardinalnum,
> +			isp_dev->sof_count,
> +			irqstatus,
> +			dmastatus,
> +			hw_frame_num,
> +			cq_num);
> +	return IRQ_HANDLED;
> +}
> +
> +static int enable_sys_clock(struct isp_p1_device *p1_dev)
> +{
> +	struct device *dev = &p1_dev->pdev->dev;
> +	int ret;
> +
> +	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
> +
> +	ret = clk_bulk_prepare_enable(p1_dev->isp_clk.num_clks,
> +				      p1_dev->isp_clk.clk_list);
> +	if (ret < 0)
> +		goto clk_err;
> +	return 0;
> +clk_err:
> +	dev_err(dev, "cannot pre-en isp_cam clock:%d\n", ret);
> +	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
> +				   p1_dev->isp_clk.clk_list);
> +	return ret;
> +}
> +
> +static void disable_sys_clock(struct isp_p1_device *p1_dev)
> +{
> +	struct device *dev = &p1_dev->pdev->dev;
> +
> +	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
> +	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
> +				   p1_dev->isp_clk.clk_list);
> +}
> +
> +static int mtk_isp_probe(struct platform_device *pdev)
> +{
> +	struct isp_p1_device *p1_dev;
> +	struct mtk_isp_p1_ctx *isp_ctx;
> +	struct isp_device *isp_dev;
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int ret;
> +	unsigned int i;
> +
> +	/* Allocate context */
> +	p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL);
> +	if (!p1_dev)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, p1_dev);
> +	isp_ctx = &p1_dev->isp_ctx;
> +	p1_dev->pdev = pdev;
> +
> +	p1_dev->isp_devs =
> +		devm_kzalloc(dev,
> +			     sizeof(struct isp_device) * ISP_DEV_NODE_NUM,
> +			     GFP_KERNEL);
> +	if (!p1_dev->isp_devs)
> +		return -ENOMEM;
> +
> +	p1_dev->cam_dev =
> +		devm_kzalloc(dev, sizeof(struct mtk_cam_dev), GFP_KERNEL);
> +	if (!p1_dev->isp_devs)
> +		return -ENOMEM;
> +
> +	/* iomap registers */
> +	for (i = ISP_CAMSYS_CONFIG_IDX; i < ISP_DEV_NODE_NUM; i++) {
> +		isp_dev = &p1_dev->isp_devs[i];
> +		isp_dev->isp_hw_module = i;
> +		isp_dev->dev = dev;
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> +		isp_dev->regs = devm_ioremap_resource(dev, res);
> +
> +		dev_info(dev, "cam%u, map_addr=0x%lx\n",
> +			 i, (unsigned long)isp_dev->regs);
> +
> +		if (!isp_dev->regs)
> +			return PTR_ERR(isp_dev->regs);
> +
> +		/* support IRQ from ISP_CAM_A_IDX */
> +		if (i >= ISP_CAM_A_IDX) {
> +			/* reg & interrupts index is shifted with 1  */
> +			isp_dev->irq = platform_get_irq(pdev, i - 1);
> +			if (isp_dev->irq > 0) {
> +				ret = devm_request_irq(dev, isp_dev->irq,
> +						       isp_irq_cam,
> +						       IRQF_SHARED,
> +						       dev_driver_string(dev),
> +						       (void *)isp_dev);
> +				if (ret) {
> +					dev_err(dev,
> +						"req_irq fail, dev:%s irq=%d\n",
> +						dev->of_node->name,
> +						isp_dev->irq);
> +					return ret;
> +				}
> +				dev_info(dev, "Registered irq=%d, ISR:%s\n",
> +					 isp_dev->irq, dev_driver_string(dev));
> +			}
> +		}
> +		spin_lock_init(&isp_dev->spinlock_irq);
> +	}
> +
> +	p1_dev->isp_clk.num_clks = ARRAY_SIZE(mtk_isp_clks);
> +	p1_dev->isp_clk.clk_list =
> +		devm_kcalloc(dev,
> +			     p1_dev->isp_clk.num_clks,
> +			     sizeof(*p1_dev->isp_clk.clk_list),
> +			     GFP_KERNEL);
> +	if (!p1_dev->isp_clk.clk_list)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < p1_dev->isp_clk.num_clks; ++i)
> +		p1_dev->isp_clk.clk_list->id = mtk_isp_clks[i];
> +
> +	ret = devm_clk_bulk_get(dev,
> +				p1_dev->isp_clk.num_clks,
> +				p1_dev->isp_clk.clk_list);
> +	if (ret) {
> +		dev_err(dev, "cannot get isp cam clock:%d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Initialize reserved DMA memory */
> +	ret = mtk_cam_reserved_memory_init(p1_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to configure DMA memory\n");
> +		return ret;
> +	}
> +
> +	/* Initialize the v4l2 common part */
> +	ret = mtk_cam_dev_init(pdev, p1_dev->cam_dev);
> +	if (ret)
> +		return ret;
> +
> +	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
> +	atomic_set(&p1_dev->isp_ctx.isp_user_cnt, 0);
> +	pm_runtime_enable(dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_isp_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct isp_p1_device *p1_dev = dev_get_drvdata(dev);
> +
> +	pm_runtime_disable(dev);
> +	mtk_cam_dev_release(pdev, p1_dev->cam_dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_isp_suspend(struct device *dev)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct isp_device *isp_dev;
> +	unsigned int reg_val;
> +	int usercount, module;
> +
> +	module = p1_dev->isp_ctx.isp_hw_module;
> +	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
> +
> +	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
> +
> +	/* If no user count, no further action */
> +	if (!usercount)
> +		return 0;
> +
> +	isp_dev = &p1_dev->isp_devs[module];
> +	reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
> +	if (reg_val & VFDATA_EN_BIT) {
> +		dev_dbg(dev, "Cam:%d suspend, disable VF\n", module);
> +		/* disable VF */
> +		writel((reg_val & (~VFDATA_EN_BIT)),
> +		       isp_dev->regs + REG_TG_VF_CON);
> +		/*
> +		 * After VF enable, The TG frame count will be reset to 0;
> +		 */
> +		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
> +		writel((reg_val & (~CMOS_EN_BIT)),
> +		       isp_dev->regs +  + REG_TG_SEN_MODE);
> +	}
> +
> +	disable_sys_clock(p1_dev);
> +
> +	return 0;
> +}
> +
> +static int mtk_isp_resume(struct device *dev)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct isp_device *isp_dev;
> +	unsigned int reg_val;
> +	int module, usercount;
> +
> +	module = p1_dev->isp_ctx.isp_hw_module;
> +	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
> +
> +	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
> +
> +	/* If no user count, no further action */
> +	if (!usercount)
> +		return 0;
> +
> +	enable_sys_clock(p1_dev);
> +
> +	/* V4L2 stream-on phase & restore HW stream-on status */
> +	if (p1_dev->cam_dev->streaming) {
> +		isp_dev = &p1_dev->isp_devs[module];
> +		dev_dbg(dev, "Cam:%d resume,enable VF\n", module);
> +		/* Enable CMOS */
> +		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
> +		writel((reg_val | CMOS_EN_BIT),
> +		       isp_dev->regs + REG_TG_SEN_MODE);
> +		/* Enable VF */
> +		reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
> +		writel((reg_val | VFDATA_EN_BIT),
> +		       isp_dev->regs + REG_TG_VF_CON);
> +	}
> +	return 0;
> +}
> +
> +static int isp_setup_scp_rproc(struct isp_p1_device *p1_dev)
> +{
> +	phandle rproc_phandle;
> +	struct device *dev = &p1_dev->pdev->dev;
> +	int ret;
> +
> +	p1_dev->scp_pdev = scp_get_pdev(p1_dev->pdev);
> +	if (!p1_dev->scp_pdev) {
> +		dev_err(dev, "Failed to get scp device\n");
> +		return -ENODEV;
> +	}
> +	ret = of_property_read_u32(dev->of_node, "mediatek,scp",
> +				   &rproc_phandle);
> +	if (ret) {
> +		dev_err(dev, "fail to get rproc_phandle:%d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	p1_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle);
> +	dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n\n",
> +		p1_dev->rproc_handle);
> +	if (!p1_dev->rproc_handle) {
> +		dev_err(dev, "fail to get rproc_handle\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = rproc_boot(p1_dev->rproc_handle);
> +	if (ret < 0) {
> +		/*
> +		 * Return 0 if downloading firmware successfully,
> +		 * otherwise it is failed
> +		 */
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int isp_init_context(struct isp_p1_device *p1_dev)
> +{
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct device *dev = &p1_dev->pdev->dev;
> +	unsigned int i;
> +
> +	dev_dbg(dev, "init irq work thread\n");
> +	if (!isp_ctx->isp_deque_thread.thread) {
> +		mutex_init(&isp_ctx->composer_tx_lock);
> +		init_waitqueue_head(&isp_ctx->isp_deque_thread.wq);
> +		isp_ctx->isp_deque_thread.thread =
> +			kthread_run(isp_deque_work, (void *)p1_dev,
> +				    "isp_deque_work");
> +		if (IS_ERR(isp_ctx->isp_deque_thread.thread)) {
> +			dev_err(dev, "unable to alloc kthread\n");
> +			isp_ctx->isp_deque_thread.thread = NULL;
> +			return -ENOMEM;
> +		}
> +	}
> +	spin_lock_init(&isp_ctx->irq_dequeue_lock);
> +
> +	INIT_LIST_HEAD(&isp_ctx->p1_enqueue_list.queue);
> +	atomic_set(&isp_ctx->p1_enqueue_list.queue_cnt, 0);
> +
> +	for (i = 0; i < ISP_DEV_NODE_NUM; i++)
> +		spin_lock_init(&p1_dev->isp_devs[i].spinlock_irq);
> +
> +	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
> +	spin_lock_init(&isp_ctx->composer_txlist.lock);
> +
> +	atomic_set(&isp_ctx->irq_data_end, 0);
> +	atomic_set(&isp_ctx->irq_data_start, 0);
> +	return 0;
> +}
> +
> +static int isp_uninit_context(struct isp_p1_device *p1_dev)
> +{
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct mtk_isp_queue_job *framejob, *tmp_framejob;
> +
> +	spin_lock_irq(&isp_ctx->p1_enqueue_list.lock);
> +	list_for_each_entry_safe(framejob, tmp_framejob,
> +				 &isp_ctx->p1_enqueue_list.queue, list_entry) {
> +		list_del(&framejob->list_entry);
> +		kfree(framejob);
> +	}
> +	spin_unlock_irq(&isp_ctx->p1_enqueue_list.lock);
> +
> +	atomic_set(&isp_ctx->isp_user_cnt, 0);
> +
> +	if (!IS_ERR(isp_ctx->isp_deque_thread.thread)) {
> +		kthread_stop(isp_ctx->isp_deque_thread.thread);
> +		wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
> +		isp_ctx->isp_deque_thread.thread = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static unsigned int get_enable_dma_ports(struct mtk_cam_dev *cam_dev)

I think s/enable/enabled would be a clearer name for both the function
and local variable.

> +{
> +	unsigned int enable_dma_ports, i;
> +
> +	/* Get the enabled meta DMA ports */
> +	enable_dma_ports = 0;
> +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> +		if (cam_dev->mem2mem2_nodes[i].enabled)
> +			enable_dma_ports |=
> +				cam_dev->mem2mem2_nodes[i].desc.dma_port;
> +	}
> +	dev_dbg(&cam_dev->pdev->dev, "%s enable_dma_ports:0x%x",
> +		__func__, enable_dma_ports);
> +
> +	return enable_dma_ports;
> +}
> +
> +/* Utility functions */
> +static unsigned int get_sensor_pixel_id(unsigned int fmt)
> +{
> +	switch (fmt) {
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> +		return raw_pxl_id_b;
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> +		return raw_pxl_id_gb;
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> +		return raw_pxl_id_gr;
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> +		return raw_pxl_id_r;
> +	default:
> +		return raw_pxl_id_b;
> +	}
> +}
> +
> +static unsigned int get_sensor_fmt(unsigned int fmt)
> +{
> +	switch (fmt) {
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +		return img_fmt_bayer8;
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +		return img_fmt_bayer10;
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +		return img_fmt_bayer12;
> +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> +		return img_fmt_bayer14;
> +	default:
> +		return img_fmt_unknown;
> +	}
> +}
> +
> +static unsigned int get_img_fmt(unsigned int fourcc)
> +{
> +	switch (fourcc) {
> +	case V4L2_PIX_FMT_MTISP_B8:
> +		return img_fmt_bayer8;
> +	case V4L2_PIX_FMT_MTISP_F8:
> +		return img_fmt_fg_bayer8;
> +	case V4L2_PIX_FMT_MTISP_B10:
> +		return img_fmt_bayer10;
> +	case V4L2_PIX_FMT_MTISP_F10:
> +		return img_fmt_fg_bayer10;
> +	case V4L2_PIX_FMT_MTISP_B12:
> +		return img_fmt_bayer12;
> +	case V4L2_PIX_FMT_MTISP_F12:
> +		return img_fmt_fg_bayer12;
> +	case V4L2_PIX_FMT_MTISP_B14:
> +		return img_fmt_bayer14;
> +	case V4L2_PIX_FMT_MTISP_F14:
> +		return img_fmt_fg_bayer14;
> +	default:
> +		return img_fmt_unknown;
> +	}
> +}
> +
> +static unsigned int get_pixel_byte(unsigned int fourcc)
> +{
> +	switch (fourcc) {
> +	case V4L2_PIX_FMT_MTISP_B8:
> +	case V4L2_PIX_FMT_MTISP_F8:
> +		return 8;
> +	case V4L2_PIX_FMT_MTISP_B10:
> +	case V4L2_PIX_FMT_MTISP_F10:
> +		return 10;
> +	case V4L2_PIX_FMT_MTISP_B12:
> +	case V4L2_PIX_FMT_MTISP_F12:
> +		return 12;
> +	case V4L2_PIX_FMT_MTISP_B14:
> +	case V4L2_PIX_FMT_MTISP_F14:
> +		return 14;
> +	case V4L2_PIX_FMT_MTISP_U8:
> +	case V4L2_PIX_FMT_MTISP_U10:
> +	case V4L2_PIX_FMT_MTISP_U12:
> +	case V4L2_PIX_FMT_MTISP_U14:
> +		return 16;
> +	default:
> +		return 10;
> +	}
> +}
> +
> +static void composer_deinit_done_cb(void *data)
> +{
> +	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
> +
> +	disable_sys_clock(p1_dev);
> +	/* Notify PM */
> +	pm_runtime_put_sync(&p1_dev->pdev->dev);
> +}
> +
> +/* ISP P1 interface functions */
> +int mtk_isp_open(struct device *dev)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	s32 usercount = atomic_inc_return(&isp_ctx->isp_user_cnt);
> +	int ret;
> +
> +	dev_dbg(dev, "%s usercount=%d\n", __func__, usercount);
> +
> +	if (usercount == 1) {
> +		ret = isp_setup_scp_rproc(p1_dev);
> +		if (ret)
> +			goto scp_err;
> +
> +		/* ISP HW INIT */
> +		isp_ctx->isp_hw_module = ISP_CAM_B_IDX;
> +		/* Use pure RAW as default HW path */
> +		isp_ctx->isp_raw_path = ISP_PURE_RAW_PATH;
> +		/* Check enabled DMAs which is configured by media setup */
> +		isp_ctx->enable_dma_ports =
> +			get_enable_dma_ports(p1_dev->cam_dev);
> +
> +		if (!isp_ctx->enable_dma_ports) {
> +			dev_dbg(dev, "No DMAs are enabled\n");
> +			ret = -EINVAL;
> +			goto scp_err;
> +		}
> +
> +		pm_runtime_get_sync(dev);
> +
> +		ret = isp_init_context(p1_dev);
> +		if (ret)
> +			goto ctx_err;
> +		ret = isp_composer_init(isp_ctx);
> +		if (ret)
> +			goto composer_err;
> +		ret = isp_composer_hw_init(isp_ctx);
> +		if (ret)
> +			goto composer_err;
> +
> +		isp_composer_meta_config(&p1_dev->isp_ctx,
> +					 isp_ctx->enable_dma_ports);
> +	}
> +
> +	return 0;
> +composer_err:
> +	isp_uninit_context(p1_dev);
> +ctx_err:
> +	pm_runtime_put_sync(dev);
> +scp_err:
> +	atomic_dec_return(&isp_ctx->isp_user_cnt);
> +	return ret;
> +}
> +
> +int mtk_isp_release(struct device *dev)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +
> +	if (atomic_dec_and_test(&p1_dev->isp_ctx.isp_user_cnt)) {
> +		isp_composer_hw_deinit(isp_ctx, composer_deinit_done_cb);
> +		isp_uninit_context(p1_dev);
> +	}
> +
> +	dev_dbg(dev, "%s usercount=%d\n", __func__,
> +		atomic_read(&p1_dev->isp_ctx.isp_user_cnt));
> +
> +	return 0;
> +}
> +
> +int mtk_isp_config(struct device *dev)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct p1_config_param config_param;
> +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> +	struct v4l2_subdev_format sd_format;
> +	unsigned int sd_width, sd_height;
> +	unsigned int enable_dma_ports, idx;
> +	int ret;
> +
> +	p1_dev->isp_devs[isp_ctx->isp_hw_module].current_frame = 0;
> +	p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count = 0;
> +
> +	isp_ctx->frame_seq_no = 1;
> +	atomic_set(&isp_ctx->composed_frame_id, 0);
> +
> +	/* Get the enabled DMA ports */
> +	enable_dma_ports = isp_ctx->enable_dma_ports;
> +	dev_dbg(dev, "%s enable_dma_ports:0x%x", __func__, enable_dma_ports);
> +
> +	/* sensor config */
> +	sd_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(cam_dev->sensor,
> +			       pad, get_fmt, NULL, &sd_format);
> +
> +	if (ret) {
> +		dev_dbg(dev, "sensor:%s g_fmt on failed:%d\n",
> +			cam_dev->sensor->entity.name, ret);
> +		return -EPERM;
> +	}
> +
> +	dev_dbg(dev,
> +		"sensor get_fmt ret=%d, w=%d, h=%d, code=0x%x, field=%d, color=%d\n",
> +		ret, sd_format.format.width, sd_format.format.height,
> +		sd_format.format.code, sd_format.format.field,
> +		sd_format.format.colorspace);
> +
> +	config_param.cfg_in_param.continuous = 0x1;
> +	config_param.cfg_in_param.subsample = 0x0;
> +	/* fix to one pixel mode in default */
> +	config_param.cfg_in_param.pixel_mode = one_pixel_mode;
> +	/* support normal pattern in default */
> +	config_param.cfg_in_param.data_pattern = 0x0;
> +
> +	config_param.cfg_in_param.crop.left = 0x0;
> +	config_param.cfg_in_param.crop.top = 0x0;
> +
> +	config_param.cfg_in_param.raw_pixel_id =
> +		get_sensor_pixel_id(sd_format.format.code);
> +	config_param.cfg_in_param.img_fmt =
> +		get_sensor_fmt(sd_format.format.code);
> +	config_param.cfg_in_param.crop.width = sd_format.format.width;
> +	config_param.cfg_in_param.crop.height = sd_format.format.height;
> +	sd_width = sd_format.format.width;
> +	sd_height = sd_format.format.height;
> +
> +	idx = MTK_CAM_P1_MAIN_STREAM_OUT;

The idx variable is unnecessary. Just use MTK_CAM_P1_... to index into
mem2mem2_nodes directly here and below.

> +	if ((enable_dma_ports & R_IMGO) == R_IMGO) {
> +		struct v4l2_format *imgo_fmt =
> +			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> +
> +		config_param.cfg_main_param.pure_raw = isp_ctx->isp_raw_path;
> +		config_param.cfg_main_param.pure_raw_pack = 1;
> +		config_param.cfg_main_param.bypass = 0;
> +
> +		config_param.cfg_main_param.output.img_fmt =
> +			get_img_fmt(imgo_fmt->fmt.pix_mp.pixelformat);
> +		config_param.cfg_main_param.output.pixel_byte =
> +			get_pixel_byte(imgo_fmt->fmt.pix_mp.pixelformat);
> +		config_param.cfg_main_param.output.size.w =
> +			imgo_fmt->fmt.pix_mp.width;
> +		config_param.cfg_main_param.output.size.h =
> +			imgo_fmt->fmt.pix_mp.height;
> +
> +		config_param.cfg_main_param.output.size.stride =
> +			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> +		config_param.cfg_main_param.output.size.xsize =
> +			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> +
> +		config_param.cfg_main_param.output.crop.left = 0x0;
> +		config_param.cfg_main_param.output.crop.top = 0x0;
> +
> +		config_param.cfg_main_param.output.crop.width = sd_width;
> +		config_param.cfg_main_param.output.crop.height = sd_height;
> +
> +		WARN_ONCE(imgo_fmt->fmt.pix_mp.width > sd_width ||
> +			  imgo_fmt->fmt.pix_mp.height > sd_height,
> +			  "img out:%d:%d in:%d:%d",
> +			  imgo_fmt->fmt.pix_mp.width,
> +			  imgo_fmt->fmt.pix_mp.height,
> +			  sd_width,
> +			  sd_height);
> +
> +		dev_dbg(dev,
> +			"imgo pixel_byte:%d img_fmt:0x%x raw:%d\n",
> +			config_param.cfg_main_param.output.pixel_byte,
> +			config_param.cfg_main_param.output.img_fmt,
> +			config_param.cfg_main_param.pure_raw);
> +		dev_dbg(dev,
> +			"imgo param:size=%0dx%0d, stride:%d,xsize:%d,crop=%0dx%0d\n",
> +			config_param.cfg_main_param.output.size.w,
> +			config_param.cfg_main_param.output.size.h,
> +			config_param.cfg_main_param.output.size.stride,
> +			config_param.cfg_main_param.output.size.xsize,
> +			config_param.cfg_main_param.output.crop.width,
> +			config_param.cfg_main_param.output.crop.height);
> +	} else {
> +		config_param.cfg_main_param.bypass = 1;
> +	}
> +
> +	idx = MTK_CAM_P1_PACKED_BIN_OUT;
> +	if ((enable_dma_ports & R_RRZO) == R_RRZO) {
> +		struct v4l2_format *rrzo_fmt =
> +			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> +
> +		config_param.cfg_resize_param.bypass = 0;
> +		config_param.cfg_resize_param.output.img_fmt =
> +			get_img_fmt(rrzo_fmt->fmt.pix_mp.pixelformat);
> +		config_param.cfg_resize_param.output.pixel_byte =
> +			get_pixel_byte(rrzo_fmt->fmt.pix_mp.pixelformat);
> +		config_param.cfg_resize_param.output.size.w =
> +			rrzo_fmt->fmt.pix_mp.width;
> +		config_param.cfg_resize_param.output.size.h =
> +			rrzo_fmt->fmt.pix_mp.height;
> +		config_param.cfg_resize_param.output.size.stride =
> +			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> +		config_param.cfg_resize_param.output.size.xsize =
> +			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> +
> +		config_param.cfg_resize_param.output.crop.left = 0x0;
> +		config_param.cfg_resize_param.output.crop.top = 0x0;
> +		config_param.cfg_resize_param.output.crop.width = sd_width;
> +		config_param.cfg_resize_param.output.crop.height = sd_height;
> +
> +		WARN_ONCE(rrzo_fmt->fmt.pix_mp.width > sd_width ||
> +			  rrzo_fmt->fmt.pix_mp.height > sd_height,
> +			  "rrz out:%d:%d in:%d:%d",
> +			  rrzo_fmt->fmt.pix_mp.width,
> +			  rrzo_fmt->fmt.pix_mp.height,
> +			  sd_width,
> +			  sd_height);
> +
> +		dev_dbg(dev, "rrzo pixel_byte:%d img_fmt:0x%x\n",
> +			config_param.cfg_resize_param.output.pixel_byte,
> +			config_param.cfg_resize_param.output.img_fmt);
> +		dev_dbg(dev,
> +			"rrzo param:size=%0dx%0d,stride:%d,xsize:%d,crop=%0dx%0d\n",
> +			config_param.cfg_resize_param.output.size.w,
> +			config_param.cfg_resize_param.output.size.h,
> +			config_param.cfg_resize_param.output.size.stride,
> +			config_param.cfg_resize_param.output.size.xsize,
> +			config_param.cfg_resize_param.output.crop.width,
> +			config_param.cfg_resize_param.output.crop.height);
> +	} else {
> +		config_param.cfg_resize_param.bypass = 1;
> +	}
> +
> +	/* Configure meta DMAs info. */
> +	config_param.cfg_meta_param.enabled_meta_dmas = enable_dma_ports;
> +
> +	isp_composer_hw_config(isp_ctx, &config_param);
> +
> +	dev_dbg(dev, "%s done\n", __func__);
> +	return 0;
> +}
> +
> +int mtk_isp_enqueue(struct device *dev,
> +		    unsigned int dma_port,
> +		    struct mtk_cam_dev_buffer *buffer)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct mtk_isp_scp_p1_cmd frameparams;
> +
> +	memset(&frameparams, 0, sizeof(frameparams));
> +
> +	frameparams.cmd_id = ISP_CMD_ENQUEUE_META;
> +	frameparams.meta_frame.enabled_dma = dma_port;
> +	frameparams.meta_frame.vb_index = buffer->vbb.vb2_buf.index;
> +	frameparams.meta_frame.meta_addr.iova = buffer->daddr;
> +	frameparams.meta_frame.meta_addr.scp_addr = buffer->scp_addr;
> +
> +	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_CMD);
> +
> +	return 0;
> +}
> +
> +int mtk_isp_req_enqueue(struct device *dev,
> +			struct mtk_cam_dev_start_param *frameparamsbase)
> +{
> +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> +	struct p1_frame_param frameparams;
> +	struct mtk_isp_queue_job *framejob;
> +	struct mtk_cam_dev_buffer **bundle_buffers;
> +	unsigned int i, idx;
> +
> +	framejob = kzalloc(sizeof(*framejob), GFP_ATOMIC);
> +	memset(framejob, 0, sizeof(*framejob));
> +	memset(&frameparams, 0, sizeof(frameparams));
> +	INIT_LIST_HEAD(&framejob->list_buf);
> +
> +	bundle_buffers = &frameparamsbase->buffers[0];
> +	frameparams.frame_seq_no = isp_ctx->frame_seq_no++;
> +	frameparams.sof_idx =
> +		p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count;
> +	framejob->request_fd = frameparamsbase->request_fd;
> +	framejob->frame_seq_no = frameparams.frame_seq_no;
> +
> +	idx = MTK_CAM_P1_META_IN_0;
> +	if (bundle_buffers[idx]) {
> +		frameparams.tuning_addr.iova =
> +			bundle_buffers[idx]->daddr;
> +		frameparams.tuning_addr.scp_addr =
> +			bundle_buffers[idx]->scp_addr;
> +		list_add_tail(&bundle_buffers[idx]->list,
> +			      &framejob->list_buf);
> +	}
> +
> +	/* Image output */
> +	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
> +	if (bundle_buffers[idx]) {
> +		frameparams.img_dma_buffers[0].buffer.iova =
> +			bundle_buffers[idx]->daddr;
> +		frameparams.img_dma_buffers[0].buffer.scp_addr =
> +			bundle_buffers[idx]->scp_addr;
> +		dev_dbg(dev, "main stream address iova:0x%x\n",
> +			frameparams.img_dma_buffers[0].buffer.iova);
> +		list_add_tail(&bundle_buffers[idx]->list,
> +			      &framejob->list_buf);
> +	}
> +
> +	/* Resize output */
> +	idx = MTK_CAM_P1_PACKED_BIN_OUT;
> +	if (bundle_buffers[idx]) {
> +		frameparams.img_dma_buffers[1].buffer.iova =
> +			bundle_buffers[idx]->daddr;
> +		frameparams.img_dma_buffers[1].buffer.scp_addr =
> +			bundle_buffers[idx]->scp_addr;
> +		dev_dbg(dev, "packed out address iova:0x%x\n",
> +			frameparams.img_dma_buffers[1].buffer.iova);
> +		list_add_tail(&bundle_buffers[idx]->list,
> +			      &framejob->list_buf);
> +	}
> +
> +	/* Meta output DMAs */
> +	for (i = 0; i < MAX_META_DMA_NODES; i++) {
> +		idx = MTK_CAM_P1_META_OUT_0 + i;
> +		if (bundle_buffers[idx]) {
> +			frameparams.meta_addrs[i].iova =
> +			  bundle_buffers[idx]->daddr;
> +			frameparams.meta_addrs[i].scp_addr =
> +			  bundle_buffers[idx]->scp_addr;
> +			list_add_tail(&bundle_buffers[idx]->list,
> +				      &framejob->list_buf);
> +		} else {
> +			frameparams.meta_addrs[i].iova = 0;
> +			frameparams.meta_addrs[i].scp_addr = 0;
> +		}
> +	}
> +
> +	spin_lock(&isp_ctx->p1_enqueue_list.lock);
> +	list_add_tail(&framejob->list_entry, &isp_ctx->p1_enqueue_list.queue);
> +	atomic_inc(&isp_ctx->p1_enqueue_list.queue_cnt);
> +	spin_unlock(&isp_ctx->p1_enqueue_list.lock);
> +
> +	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_FRAME);
> +	dev_dbg(dev, "request fd:%d frame_seq_no:%d is queued cnt:%d\n",
> +		frameparamsbase->request_fd,
> +		frameparams.frame_seq_no,
> +		atomic_read(&isp_ctx->p1_enqueue_list.queue_cnt));
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_isp_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_suspend, mtk_isp_resume)
> +	SET_RUNTIME_PM_OPS(mtk_isp_suspend, mtk_isp_resume, NULL)
> +};
> +
> +static struct platform_driver mtk_isp_driver = {
> +	.probe   = mtk_isp_probe,
> +	.remove  = mtk_isp_remove,
> +	.driver  = {
> +		.name  = "mtk-cam",
> +		.of_match_table = of_match_ptr(mtk_isp_of_ids),
> +		.pm     = &mtk_isp_pm_ops,
> +	}
> +};
> +
> +module_platform_driver(mtk_isp_driver);
> +
> +MODULE_DESCRIPTION("Camera ISP driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> new file mode 100644
> index 000000000000..6cf8bb4ba93a
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> @@ -0,0 +1,300 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018 MediaTek Inc.
> + * Author: Ryan Yu <ryan.yu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __CAMERA_ISP_H
> +#define __CAMERA_ISP_H
> +
> +#include <linux/cdev.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioctl.h>
> +#include <linux/irqreturn.h>
> +#include <linux/miscdevice.h>
> +#include <linux/pm_qos.h>
> +#include <linux/scatterlist.h>
> +
> +#include "mtk_cam-dev.h"
> +#include "mtk_cam-scp.h"
> +
> +#define CAM_A_MAX_WIDTH		3328U
> +#define CAM_A_MAX_HEIGHT	2496U
> +#define CAM_B_MAX_WIDTH		5376U
> +#define CAM_B_MAX_HEIGHT	4032U
> +
> +#define CAM_MIN_WIDTH		80U
> +#define CAM_MIN_HEIGHT		60U
> +
> +#define IMG_MAX_WIDTH		CAM_B_MAX_WIDTH
> +#define IMG_MAX_HEIGHT		CAM_B_MAX_HEIGHT
> +#define IMG_MIN_WIDTH		CAM_MIN_WIDTH
> +#define IMG_MIN_HEIGHT		CAM_MIN_HEIGHT
> +
> +#define RRZ_MAX_WIDTH		CAM_B_MAX_WIDTH
> +#define RRZ_MAX_HEIGHT		CAM_B_MAX_HEIGHT
> +#define RRZ_MIN_WIDTH		CAM_MIN_WIDTH
> +#define RRZ_MIN_HEIGHT		CAM_MIN_HEIGHT
> +
> +#define R_IMGO		BIT(0)
> +#define R_RRZO		BIT(1)
> +#define R_AAO		BIT(3)
> +#define R_AFO		BIT(4)
> +#define R_LCSO		BIT(5)
> +#define R_PDO		BIT(6)
> +#define R_LMVO		BIT(7)
> +#define R_FLKO		BIT(8)
> +#define R_RSSO		BIT(9)
> +#define R_PSO		BIT(10)
> +
> +#define ISP_COMPOSING_MAX_NUM		4
> +#define ISP_FRAME_COMPOSING_MAX_NUM	3
> +
> +#define IRQ_DATA_BUF_SIZE		4
> +#define COMPOSRE_EVENT_BUF_SIZE		4
> +
> +#define CQ_ADDRESS_OFFSET		0x640
> +#define CQ_BUFFER_COUNT			3

Please align macro values using tabs.

> +
> +#define IRQ_STAT_STR "cam%c, SOF_%d irq(0x%x), " \
> +			"dma(0x%x), frame_num(%d)/cq_num(%d)\n"
> +
> +/*
> + * In order with the sequence of device nodes defined in dtsi rule,
> + * one hardware module should be mapping to one node.
> + */
> +enum isp_dev_node_enum {
> +	ISP_CAMSYS_CONFIG_IDX = 0,
> +	ISP_CAM_UNI_IDX,
> +	ISP_CAM_A_IDX,
> +	ISP_CAM_B_IDX,
> +	ISP_DEV_NODE_NUM
> +};
> +
> +/* Image RAW path for ISP P1 module. */
> +enum isp_raw_path_enum {
> +	ISP_PROCESS_RAW_PATH = 0,
> +	ISP_PURE_RAW_PATH
> +};
> +
> +enum {
> +	img_fmt_unknown		= 0x0000,
> +	img_fmt_raw_start	= 0x2200,
> +	img_fmt_bayer8		= img_fmt_raw_start,
> +	img_fmt_bayer10,
> +	img_fmt_bayer12,
> +	img_fmt_bayer14,
> +	img_fmt_fg_bayer8,
> +	img_fmt_fg_bayer10,
> +	img_fmt_fg_bayer12,
> +	img_fmt_fg_bayer14,
> +};
> +
> +enum {
> +	raw_pxl_id_b   = 0,
> +	raw_pxl_id_gb,
> +	raw_pxl_id_gr,
> +	raw_pxl_id_r
> +};
> +
> +enum {
> +	default_pixel_mode = 0,
> +	one_pixel_mode,
> +	two_pixel_mode,
> +	four_pixel_mode,
> +	pixel_mode_num,
> +};
> +
> +enum mtk_isp_scp_ipi_type {
> +	SCP_ISP_CMD = 0,
> +	SCP_ISP_FRAME,
> +};
> +
> +struct isp_queue {
> +	struct list_head queue;
> +	atomic_t queue_cnt;
> +	spinlock_t lock; /* queue attributes protection */
> +};
> +
> +struct isp_thread {
> +	struct task_struct *thread;
> +	wait_queue_head_t wq;
> +};
> +
> +struct mtk_isp_queue_work {
> +	union {
> +		struct mtk_isp_scp_p1_cmd cmd;
> +		struct p1_frame_param frameparams;
> +	};
> +	struct list_head list_entry;
> +	enum mtk_isp_scp_ipi_type type;
> +};
> +
> +struct mtk_cam_dev_stat_event_data {
> +	__u32 frame_seq_no;
> +	__u32 meta0_vb2_index;
> +	__u32 meta1_vb2_index;
> +	__u32 irq_status_mask;
> +	__u32 dma_status_mask;
> +};
> +
> +struct mtk_isp_queue_job {
> +	struct list_head list_entry;
> +	struct list_head list_buf;
> +	unsigned int request_fd;
> +	unsigned int frame_seq_no;
> +};
> +
> +struct isp_clk_struct {
> +	int num_clks;
> +	struct clk_bulk_data *clk_list;
> +};
> +
> +struct isp_device {
> +	struct device *dev;
> +	void __iomem *regs;
> +	int irq;
> +	spinlock_t spinlock_irq; /* ISP reg setting integrity */
> +	unsigned int current_frame;
> +	unsigned int meta0_vb2_index;
> +	unsigned int meta1_vb2_index;
> +	u8 sof_count;
> +	u8 isp_hw_module;
> +};
> +
> +struct mtk_isp_p1_ctx {
> +	struct isp_queue composer_txlist;
> +	struct isp_thread composer_tx_thread;
> +	atomic_t cmd_queued;
> +	struct mutex composer_tx_lock; /* isp composer work protection */
> +
> +	struct isp_thread composer_rx_thread;
> +	struct mtk_isp_scp_p1_cmd composer_evts[COMPOSRE_EVENT_BUF_SIZE];
> +	atomic_t composer_evts_start;
> +	atomic_t composer_evts_end;
> +	spinlock_t composer_evts_lock; /* SCP events protection */
> +	/* increase after ipi */
> +	atomic_t ipi_occupied;
> +	/* increase after frame enqueue */
> +	atomic_t composing_frame;
> +	/* current composed frame id */
> +	atomic_t composed_frame_id;
> +
> +	struct isp_queue p1_enqueue_list;
> +
> +	struct isp_thread isp_deque_thread;
> +	struct mtk_cam_dev_stat_event_data irq_event_datas[IRQ_DATA_BUF_SIZE];
> +	atomic_t irq_data_start;
> +	atomic_t irq_data_end;
> +	spinlock_t irq_dequeue_lock; /* ISP frame dequeuq protection */
> +
> +	dma_addr_t scp_mem_pa;
> +	dma_addr_t scp_mem_iova;
> +	struct sg_table sgtable;
> +
> +	/* increase after open, decrease when close */
> +	atomic_t isp_user_cnt;
> +	/* frame sequence number, increase per en-queue*/
> +	int frame_seq_no;
> +	unsigned int isp_hw_module;
> +	unsigned int isp_raw_path;
> +	unsigned int enable_dma_ports;
> +
> +	void (*composer_deinit_donecb)(void *isp_ctx);
> +
> +	struct list_head list;
> +};
> +
> +struct isp_p1_device {
> +	struct platform_device *pdev;
> +
> +	/* for SCP driver  */
> +	struct platform_device *scp_pdev;
> +	struct rproc *rproc_handle;
> +
> +	struct mtk_isp_p1_ctx isp_ctx;
> +	struct isp_clk_struct isp_clk;

What's the benefit of having mtk_isp_p1_ctx and isp_clk_struct in
separate structs? They are only ever used in isp_p1_device.

> +	struct mtk_cam_dev *cam_dev;
> +	struct isp_device *isp_devs;

Similarly, why are these allocated at runtime rather then just members
of the struct?

In mtk_isp_probe the struct isp_p1_device is allocated, and immediately
afterwards the struct mtk_cam_dev and struct isp_devices are. There will
only ever be ISP_DEV_NODE_NUM isp_devices.

Could this be changed to:
	struct mtk_cam_dev cam_dev;
	struct isp_device isp_devs[ISP_DEV_NODE_NUM];
?

> +};
> +
> +static inline struct isp_p1_device *
> +p1_ctx_to_dev(const struct mtk_isp_p1_ctx *__p1_ctx)
> +{
> +	return container_of(__p1_ctx, struct isp_p1_device, isp_ctx);
> +}
> +
> +static inline struct isp_p1_device *get_p1_device(struct device *dev)
> +{
> +	return ((struct isp_p1_device *)dev_get_drvdata(dev));
> +}
> +
> +int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx);
> +int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx);
> +void isp_composer_meta_config(struct mtk_isp_p1_ctx *isp_ctx,
> +			      unsigned int dma);
> +void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
> +			    struct p1_config_param *config_param);
> +void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on);
> +void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
> +			    void (*donecb)(void *data));
> +void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
> +			  void *data,
> +			  enum mtk_isp_scp_ipi_type type);

These functions are declared here, but implemented in mtk_cam-scp.c.
Can the funtion declarations be moved to mtk_cam-scp.h?

> +
> +/**
> + * mtk_isp_open - open isp driver and initialize related resources.
> + *
> + * @dev:	isp device.
> + *
> + */
> +int mtk_isp_open(struct device *dev);
> +
> +/**
> + * mtk_isp_release - release isp driver and related resources.
> + *
> + * @dev:	isp device.
> + *
> + */
> +int mtk_isp_release(struct device *dev);
> +
> +/**
> + * mtk_isp_config - output image & meta data configuration.
> + *
> + * @dev:	isp device.
> + *
> + */
> +int mtk_isp_config(struct device *dev);
> +
> +/**
> + * mtk_isp_req_enqueue - enqueue a frame bundle (per-frame basis) to ISP driver.
> + *
> + * @dev:	isp device.
> + * @frameparamsbase: pointer to &struct mtk_cam_dev_start_param.
> + *
> + */
> +int mtk_isp_req_enqueue(struct device *dev,
> +			struct mtk_cam_dev_start_param *frameparamsbase);
> +
> +/**
> + * mtk_isp_enqueue - enqueue a single frame to ISP driver
> + * for non-per-frame DMA.
> + *
> + * @dev:	isp device.
> + * @buffer: pointer to &struct mtk_cam_dev_buffer.
> + *
> + */
> +int mtk_isp_enqueue(struct device *dev,
> +		    unsigned int dma_idx,
> +		    struct mtk_cam_dev_buffer *buffer);
> +#endif /*__CAMERA_ISP_H*/
> -- 
> 2.18.0
> 

_______________________________________________
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] 104+ messages in thread

* Re: [RFC, V2, 09/11] media: platform: Add Mediatek ISP P1 device driver
  2019-05-24 21:19   ` [RFC, V2, 09/11] " Drew Davenport
@ 2019-05-27 13:07     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-27 13:07 UTC (permalink / raw)
  To: Drew Davenport
  Cc: ryan.yu, Jerry-ch.Chen, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, frankie.chiu, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, sj.huang, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, shik, tfiga, christie.yu, zwisler

Hi, Drew:


On Fri, 2019-05-24 at 15:19 -0600, Drew Davenport wrote:
> Hi Jungo,
> 
> On Fri, May 10, 2019 at 09:58:04AM +0800, Jungo Lin wrote:
> > This patch adds the Mediatek ISP P1 HW control device driver.
> > It handles the ISP HW configuration, provides interrupt handling and
> > initializes the V4L2 device nodes and other functions.
> 
> A few comments inline.
> 

Appreciate your feedback on this patch set firstly.

> > 
> > Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> > ---
> >  .../mtk-isp/isp_50/cam/mtk_cam-regs.h         |  149 ++
> >  .../platform/mtk-isp/isp_50/cam/mtk_cam.c     | 1206 +++++++++++++++++
> >  .../platform/mtk-isp/isp_50/cam/mtk_cam.h     |  300 ++++
> >  3 files changed, 1655 insertions(+)
> >  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> >  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> >  create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> > 
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> > new file mode 100644
> > index 000000000000..342f0e0e9837
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> > @@ -0,0 +1,149 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Ryan Yu <ryan.yu@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef _CAM_REGS_H
> > +#define _CAM_REGS_H
> > +
> > +/* TG Bit Mask */
> > +#define VFDATA_EN_BIT	BIT(0)
> > +#define CMOS_EN_BIT	BIT(0)
> > +
> > +/* normal signal bit */
> > +#define VS_INT_ST	BIT(0)
> > +#define HW_PASS1_DON_ST	BIT(11)
> > +#define SOF_INT_ST	BIT(12)
> > +#define SW_PASS1_DON_ST	BIT(30)
> > +
> > +/* err status bit */
> > +#define TG_ERR_ST	BIT(4)
> > +#define TG_GBERR_ST	BIT(5)
> > +#define CQ_CODE_ERR_ST	BIT(6)
> > +#define CQ_APB_ERR_ST	BIT(7)
> > +#define CQ_VS_ERR_ST	BIT(8)
> > +#define AMX_ERR_ST	BIT(15)
> > +#define RMX_ERR_ST	BIT(16)
> > +#define BMX_ERR_ST	BIT(17)
> > +#define RRZO_ERR_ST	BIT(18)
> > +#define AFO_ERR_ST	BIT(19)
> > +#define IMGO_ERR_ST	BIT(20)
> > +#define AAO_ERR_ST	BIT(21)
> > +#define PSO_ERR_ST	BIT(22)
> > +#define LCSO_ERR_ST	BIT(23)
> > +#define BNR_ERR_ST	BIT(24)
> > +#define LSCI_ERR_ST	BIT(25)
> > +#define DMA_ERR_ST	BIT(29)
> > +
> > +/* CAM DMA done status */
> > +#define FLKO_DONE_ST	BIT(4)
> > +#define AFO_DONE_ST	BIT(5)
> > +#define AAO_DONE_ST	BIT(7)
> > +#define PSO_DONE_ST	BIT(14)
> 
> Please align the values using tabs here and elsewhere.
> 

Ok, we will revise this coding style issue in next patch.

> > +
> > +/* IRQ signal mask */
> > +#define INT_ST_MASK_CAM	( \
> > +			VS_INT_ST |\
> > +			SOF_INT_ST |\
> > +			HW_PASS1_DON_ST |\
> > +			SW_PASS1_DON_ST)
> > +
> > +/* IRQ Warning Mask */
> > +#define INT_ST_MASK_CAM_WARN	(\
> > +				RRZO_ERR_ST |\
> > +				AFO_ERR_ST |\
> > +				IMGO_ERR_ST |\
> > +				AAO_ERR_ST |\
> > +				PSO_ERR_ST | \
> > +				LCSO_ERR_ST |\
> > +				BNR_ERR_ST |\
> > +				LSCI_ERR_ST)
> > +
> > +/* IRQ Error Mask */
> > +#define INT_ST_MASK_CAM_ERR	(\
> > +				TG_ERR_ST |\
> > +				TG_GBERR_ST |\
> > +				CQ_CODE_ERR_ST |\
> > +				CQ_APB_ERR_ST |\
> > +				CQ_VS_ERR_ST |\
> > +				BNR_ERR_ST |\
> > +				RMX_ERR_ST |\
> > +				BMX_ERR_ST |\
> > +				BNR_ERR_ST |\
> > +				LSCI_ERR_ST |\
> > +				DMA_ERR_ST)
> > +
> > +/* IRQ Signal Log Mask */
> > +#define INT_ST_LOG_MASK_CAM	(\
> > +				SOF_INT_ST |\
> > +				SW_PASS1_DON_ST |\
> > +				VS_INT_ST |\
> > +				TG_ERR_ST |\
> > +				TG_GBERR_ST |\
> > +				RRZO_ERR_ST |\
> > +				AFO_ERR_ST |\
> > +				IMGO_ERR_ST |\
> > +				AAO_ERR_ST |\
> > +				DMA_ERR_ST)
> > +
> > +/* DMA Event Notification Mask */
> > +#define DMA_ST_MASK_CAM	(\
> > +			AFO_DONE_ST |\
> > +			AAO_DONE_ST |\
> > +			PSO_DONE_ST |\
> > +			FLKO_DONE_ST)
> > +
> > +/* Status check */
> > +#define REG_CTL_EN		0x0004
> > +#define REG_CTL_DMA_EN		0x0008
> > +#define REG_CTL_FMT_SEL		0x0010
> > +#define REG_CTL_EN2		0x0018
> > +#define REG_CTL_RAW_INT_EN	0x0020
> > +#define REG_CTL_RAW_INT_STAT	0x0024
> > +#define REG_CTL_RAW_INT2_STAT	0x0034
> > +#define REG_CTL_RAW_INT3_STAT	0x00c4
> > +#define REG_CTL_TWIN_STAT	0x0050
> > +
> > +#define REG_TG_SEN_MODE		0x0230
> > +#define REG_TG_SEN_GRAB_PIX	0x0238
> > +#define REG_TG_SEN_GRAB_LIN	0x023c
> > +#define REG_TG_VF_CON		0x0234
> > +#define REG_TG_SUB_PERIOD	0x02a4
> > +
> > +#define REG_IMGO_BASE_ADDR	0x1020
> > +#define REG_RRZO_BASE_ADDR	0x1050
> > +
> > +/* Error status log */
> > +#define REG_IMGO_ERR_STAT	0x1360
> > +#define REG_RRZO_ERR_STAT	0x1364
> > +#define REG_AAO_ERR_STAT	0x1368
> > +#define REG_AFO_ERR_STAT	0x136c
> > +#define REG_LCSO_ERR_STAT	0x1370
> > +#define REG_UFEO_ERR_STAT	0x1374
> > +#define REG_PDO_ERR_STAT	0x1378
> > +#define REG_BPCI_ERR_STAT	0x137c
> > +#define REG_LSCI_ERR_STAT	0x1384
> > +#define REG_PDI_ERR_STAT	0x138c
> > +#define REG_LMVO_ERR_STAT	0x1390
> > +#define REG_FLKO_ERR_STAT	0x1394
> > +#define REG_PSO_ERR_STAT	0x13a0
> > +
> > +/* ISP command */
> > +#define REG_CQ_THR0_BASEADDR	0x0198
> > +#define REG_HW_FRAME_NUM	0x13b8
> > +
> > +/* META */
> > +#define REG_META0_VB2_INDEX	0x14dc
> > +#define REG_META1_VB2_INDEX	0x151c
> > +
> > +#endif	/* _CAM_REGS_H */
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> > new file mode 100644
> > index 000000000000..fc874ec8f7f0
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> > @@ -0,0 +1,1206 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Ryan Yu <ryan.yu@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/atomic.h>
> > +#include <linux/cdev.h>
> > +#include <linux/compat.h>
> > +#include <linux/fs.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/jiffies.h>
> > +#include <linux/kernel.h>
> > +#include <linux/ktime.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_address.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/mtk_scp.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/remoteproc.h>
> > +#include <linux/sched/clock.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +#include <linux/vmalloc.h>
> > +
> > +#include "mtk_cam.h"
> > +#include "mtk_cam-regs.h"
> > +#include "mtk_cam-smem.h"
> > +
> > +static const struct of_device_id mtk_isp_of_ids[] = {
> > +	{.compatible = "mediatek,mt8183-camisp",},
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_isp_of_ids);
> > +
> > +/* list of clocks required by isp cam */
> > +static const char * const mtk_isp_clks[] = {
> > +	"CAMSYS_CAM_CGPDN", "CAMSYS_CAMTG_CGPDN"
> > +};
> > +
> > +static void isp_dump_dma_status(struct isp_device *isp_dev)
> > +{
> > +	dev_err(isp_dev->dev,
> > +		"IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n",
> > +		readl(isp_dev->regs + REG_IMGO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_RRZO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_AAO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_AFO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_LMVO_ERR_STAT));
> > +	dev_err(isp_dev->dev,
> > +		"LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n",
> > +		readl(isp_dev->regs + REG_LCSO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_PSO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_FLKO_ERR_STAT),
> > +		readl(isp_dev->regs + REG_BPCI_ERR_STAT),
> > +		readl(isp_dev->regs + REG_LSCI_ERR_STAT));
> > +}
> > +
> > +static void mtk_isp_notify(struct mtk_isp_p1_ctx *isp_ctx,
> > +			   unsigned int request_fd,
> > +			   unsigned int frame_seq_no,
> > +			   struct list_head *list_buf,
> > +			   enum vb2_buffer_state state)
> > +{
> > +	struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx);
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	struct mtk_cam_dev_finish_param fram_param;
> > +
> > +	fram_param.list_buf = list_buf;
> > +	fram_param.request_fd = request_fd;
> > +	fram_param.frame_seq_no = frame_seq_no;
> > +	fram_param.state = state;
> > +	dev_dbg(dev, "request fd:%d frame_seq_no:%d\n",
> > +		fram_param.request_fd,
> > +		fram_param.frame_seq_no);
> > +	mtk_cam_dev_job_finish(p1_dev->cam_dev, &fram_param);
> > +}
> > +
> > +static void isp_deque_frame(struct isp_p1_device *p1_dev,
> > +			    unsigned int node_id, int vb2_index,
> > +			    int frame_seq_no)
> > +{
> > +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	struct vb2_queue *vb2_queue = &cam_dev->mem2mem2_nodes[node_id].vbq;
> > +	struct vb2_buffer *vb;
> > +	struct vb2_v4l2_buffer *vbb;
> > +
> > +	if (!cam_dev->mem2mem2_nodes[node_id].enabled)
> > +		return;
> > +
> > +	mutex_lock(vb2_queue->lock);
> > +	list_for_each_entry(vb, &vb2_queue->queued_list, queued_entry) {
> > +		vbb = to_vb2_v4l2_buffer(vb);
> > +		if (vbb->request_fd < 0 &&
> > +		    vb->index == vb2_index &&
> > +		    vb->state == VB2_BUF_STATE_ACTIVE) {
> > +			dev_dbg(dev, "%s:%d:%d", __func__, node_id, vb2_index);
> > +			vbb->vb2_buf.timestamp = ktime_get_ns();
> > +			vbb->sequence = frame_seq_no;
> > +			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
> > +		}
> > +	}
> > +	mutex_unlock(vb2_queue->lock);
> > +}
> > +
> > +static void isp_deque_request_frame(struct isp_p1_device *p1_dev,
> > +				    int frame_seq_no)
> > +{
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	struct mtk_isp_queue_job *framejob, *tmp;
> > +	struct isp_queue *p1_enqueue_list = &isp_ctx->p1_enqueue_list;
> > +
> > +	/* Match dequeue work and enqueue frame */
> > +	spin_lock(&p1_enqueue_list->lock);
> > +	list_for_each_entry_safe(framejob, tmp, &p1_enqueue_list->queue,
> > +				 list_entry) {
> > +		dev_dbg(dev,
> > +			"%s frame_seq_no:%d, target frame_seq_no:%d\n",
> > +			__func__,
> > +			framejob->frame_seq_no, frame_seq_no);
> > +		/* Match by the en-queued request number */
> > +		if (framejob->frame_seq_no == frame_seq_no) {
> > +			/* Pass to user space */
> > +			mtk_isp_notify(isp_ctx,
> > +				       framejob->request_fd,
> > +				       framejob->frame_seq_no,
> > +				       &framejob->list_buf,
> > +				       VB2_BUF_STATE_DONE);
> > +			atomic_dec(&p1_enqueue_list->queue_cnt);
> > +			dev_dbg(dev,
> > +				"frame_seq_no:%d is done, queue_cnt:%d\n",
> > +				framejob->frame_seq_no,
> > +				atomic_read(&p1_enqueue_list->queue_cnt));
> > +
> > +			/* remove only when frame ready */
> > +			list_del(&framejob->list_entry);
> > +			kfree(framejob);
> > +			break;
> > +		} else if (framejob->frame_seq_no < frame_seq_no) {
> > +			/* Pass to user space for frame drop */
> > +			mtk_isp_notify(isp_ctx,
> > +				       framejob->request_fd,
> > +				       framejob->frame_seq_no,
> > +				       &framejob->list_buf,
> > +				       VB2_BUF_STATE_ERROR);
> > +			atomic_dec(&p1_enqueue_list->queue_cnt);
> > +			dev_dbg(dev,
> > +				"frame_seq_no:%d drop, queue_cnt:%d\n",
> > +				framejob->frame_seq_no,
> > +				atomic_read(&p1_enqueue_list->queue_cnt));
> > +
> > +			/* remove only drop frame */
> > +			list_del(&framejob->list_entry);
> > +			kfree(framejob);
> > +		}
> > +	}
> > +	spin_unlock(&p1_enqueue_list->lock);
> > +}
> > +
> > +static int isp_deque_work(void *data)
> > +{
> > +	struct isp_p1_device *p1_dev = (struct isp_p1_device *)data;
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> > +	struct mtk_cam_dev_stat_event_data event_data;
> > +	atomic_t *irq_data_end = &isp_ctx->irq_data_end;
> > +	atomic_t *irq_data_start = &isp_ctx->irq_data_start;
> > +	unsigned long flags;
> > +	int ret, i;
> > +
> > +	while (1) {
> > +		ret = wait_event_interruptible(isp_ctx->isp_deque_thread.wq,
> > +					       (atomic_read(irq_data_end) !=
> > +					       atomic_read(irq_data_start)) ||
> > +					       kthread_should_stop());
> > +
> > +		if (kthread_should_stop())
> > +			break;
> > +
> > +		if (ret == ERESTARTSYS) {
> > +			dev_err(dev, "interrupted by a signal!\n");
> > +			continue;
> > +		}
> > +
> > +		spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
> > +		i = atomic_read(&isp_ctx->irq_data_start);
> > +		memcpy(&event_data, &isp_ctx->irq_event_datas[i],
> > +		       sizeof(event_data));
> > +		memset(&isp_ctx->irq_event_datas[i], 0x00, sizeof(event_data));
> > +		atomic_set(&isp_ctx->irq_data_start, ++i & 0x3);
> > +		spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
> > +
> > +		if (event_data.irq_status_mask & VS_INT_ST) {
> > +			/* Notify specific HW events to user space */
> > +			mtk_cam_dev_event_frame_sync(cam_dev,
> > +						     event_data.frame_seq_no);
> > +			dev_dbg(dev,
> > +				"event IRQ:0x%x DMA:0x%x is sent\n",
> > +				event_data.irq_status_mask,
> > +				event_data.dma_status_mask);
> > +		}
> > +
> > +		if (event_data.dma_status_mask & AAO_DONE_ST) {
> > +			isp_deque_frame(p1_dev,
> > +					MTK_CAM_P1_META_OUT_0,
> > +					event_data.meta0_vb2_index,
> > +					event_data.frame_seq_no);
> > +		}
> > +
> > +		if (event_data.irq_status_mask & SW_PASS1_DON_ST) {
> > +			isp_deque_frame(p1_dev,
> > +					MTK_CAM_P1_META_OUT_0,
> > +					event_data.meta0_vb2_index,
> > +					event_data.frame_seq_no);
> > +
> > +			isp_deque_request_frame(p1_dev,
> > +						event_data.frame_seq_no);
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int irq_handle_sof(struct isp_device *isp_dev,
> > +			  dma_addr_t base_addr,
> > +			  unsigned int frame_num)
> > +{
> > +	unsigned int cq_addr_index;
> > +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> > +	int cq_num = atomic_read(&p1_dev->isp_ctx.composed_frame_id);
> > +
> > +	if (cq_num > frame_num) {
> > +		cq_addr_index = frame_num % CQ_BUFFER_COUNT;
> > +
> > +		writel(base_addr +
> > +			(dma_addr_t)(CQ_ADDRESS_OFFSET * cq_addr_index),
> > +			isp_dev->regs + REG_CQ_THR0_BASEADDR);
> > +		dev_dbg(isp_dev->dev,
> > +			"SOF_INT_ST, update next, cq_num:%d, frame_num:%d cq_addr:%d",
> > +			cq_num, frame_num, cq_addr_index);
> > +	} else {
> > +		dev_dbg(isp_dev->dev,
> > +			"SOF_INT_ST, wait next, cq_num:%d, frame_num:%d",
> > +			cq_num, frame_num);
> > +	}
> > +
> > +	isp_dev->sof_count += 1;
> > +
> > +	return cq_num;
> > +}
> > +
> > +static int irq_handle_notify_event(struct isp_device *isp_dev,
> > +				   unsigned int irqstatus,
> > +				   unsigned int dmastatus)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	unsigned long flags;
> > +	int i;
> > +
> > +	spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags);
> > +	i = atomic_read(&isp_ctx->irq_data_end);
> > +	isp_ctx->irq_event_datas[i].frame_seq_no = isp_dev->current_frame;
> > +	isp_ctx->irq_event_datas[i].meta0_vb2_index = isp_dev->meta0_vb2_index;
> > +	isp_ctx->irq_event_datas[i].meta1_vb2_index = isp_dev->meta1_vb2_index;
> > +	isp_ctx->irq_event_datas[i].irq_status_mask |=
> > +		(irqstatus & INT_ST_MASK_CAM);
> > +	isp_ctx->irq_event_datas[i].dma_status_mask |=
> > +		(dmastatus & DMA_ST_MASK_CAM);
> > +	atomic_set(&isp_ctx->irq_data_end, ++i & 0x3);
> > +	spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags);
> > +
> > +	wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
> > +
> > +	dev_dbg(isp_dev->dev,
> > +		"%s IRQ:0x%x DMA:0x%x seq:%d idx0:%d idx1:%d\n",
> > +		__func__,
> > +		(irqstatus & INT_ST_MASK_CAM),
> > +		(dmastatus & DMA_ST_MASK_CAM),
> > +		isp_dev->current_frame,
> > +		isp_dev->meta0_vb2_index,
> > +		isp_dev->meta1_vb2_index);
> > +
> > +	return 0;
> > +}
> > +
> > +irqreturn_t isp_irq_cam(int irq, void *data)
> > +{
> > +	struct isp_device *isp_dev = (struct isp_device *)data;
> > +	struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct device *dev = isp_dev->dev;
> > +	unsigned int cardinalnum, cq_num, hw_frame_num;
> > +	unsigned int meta0_vb2_index, meta1_vb2_index;
> > +	unsigned int irqstatus, errstatus, warnstatus, dmastatus;
> > +	unsigned long flags;
> > +
> > +	/* Check the streaming is off or not */
> > +	if (!p1_dev->cam_dev->streaming)
> > +		return IRQ_HANDLED;
> > +
> > +	cardinalnum = isp_dev->isp_hw_module - ISP_CAM_A_IDX;
> > +	cq_num = 0;
> > +
> > +	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
> > +	irqstatus = readl(isp_dev->regs + REG_CTL_RAW_INT_STAT);
> > +	dmastatus = readl(isp_dev->regs + REG_CTL_RAW_INT2_STAT);
> > +	hw_frame_num = readl(isp_dev->regs + REG_HW_FRAME_NUM);
> > +	meta0_vb2_index = readl(isp_dev->regs + REG_META0_VB2_INDEX);
> > +	meta1_vb2_index = readl(isp_dev->regs + REG_META1_VB2_INDEX);
> > +	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
> > +
> > +	/* Ignore unnecessary IRQ */
> > +	if (irqstatus == 0)
> > +		return IRQ_HANDLED;
> > +
> > +	errstatus = irqstatus & INT_ST_MASK_CAM_ERR;
> > +	warnstatus = irqstatus & INT_ST_MASK_CAM_WARN;
> > +	irqstatus = irqstatus & INT_ST_MASK_CAM;
> > +
> > +	/* sof , done order check . */
> > +	spin_lock_irqsave(&isp_dev->spinlock_irq, flags);
> > +	if ((irqstatus & HW_PASS1_DON_ST) && (irqstatus & SOF_INT_ST)) {
> > +		dev_warn(dev,
> > +			 "isp sof_don block, sof_cnt:%d\n",
> > +			 isp_dev->sof_count);
> > +
> > +		/* Notify IRQ event and enqueue ready frame */
> > +		irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
> > +		isp_dev->current_frame = hw_frame_num;
> > +		isp_dev->meta0_vb2_index = meta0_vb2_index;
> > +		isp_dev->meta1_vb2_index = meta1_vb2_index;
> > +	} else {
> > +		if (irqstatus & SOF_INT_ST) {
> > +			isp_dev->current_frame = hw_frame_num;
> > +			isp_dev->meta0_vb2_index = meta0_vb2_index;
> > +			isp_dev->meta1_vb2_index = meta1_vb2_index;
> > +		}
> > +
> > +		if ((irqstatus & INT_ST_MASK_CAM) ||
> > +		    (dmastatus & DMA_ST_MASK_CAM))
> > +			irq_handle_notify_event(isp_dev, irqstatus, dmastatus);
> > +	}
> > +	spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags);
> > +
> > +	if (irqstatus & SOF_INT_ST)
> > +		cq_num = irq_handle_sof(isp_dev, isp_ctx->scp_mem_iova,
> > +					hw_frame_num);
> > +
> > +	if (irqstatus & SW_PASS1_DON_ST) {
> > +		int num = atomic_dec_return(&isp_ctx->composing_frame);
> > +
> > +		dev_dbg(dev, "SW_PASS1_DON_ST queued frame:%d\n", num);
> > +		/* Notify TX thread to send if TX frame is blocked */
> > +		wake_up_interruptible
> > +				(&isp_ctx->composer_tx_thread.wq);
> > +	}
> > +
> > +	/* check ISP error status */
> > +	if (errstatus) {
> > +		dev_err(dev,
> > +			"raw_int_err:0x%x/0x%x/0x%x\n",
> > +			irqstatus, warnstatus, errstatus);
> > +
> > +		/* show DMA errors in detail */
> > +		if (errstatus & DMA_ERR_ST)
> > +			isp_dump_dma_status(isp_dev);
> > +	}
> > +
> > +	if (irqstatus & INT_ST_LOG_MASK_CAM)
> > +		dev_dbg(dev, IRQ_STAT_STR,
> > +			'A' + cardinalnum,
> > +			isp_dev->sof_count,
> > +			irqstatus,
> > +			dmastatus,
> > +			hw_frame_num,
> > +			cq_num);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int enable_sys_clock(struct isp_p1_device *p1_dev)
> > +{
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	int ret;
> > +
> > +	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
> > +
> > +	ret = clk_bulk_prepare_enable(p1_dev->isp_clk.num_clks,
> > +				      p1_dev->isp_clk.clk_list);
> > +	if (ret < 0)
> > +		goto clk_err;
> > +	return 0;
> > +clk_err:
> > +	dev_err(dev, "cannot pre-en isp_cam clock:%d\n", ret);
> > +	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
> > +				   p1_dev->isp_clk.clk_list);
> > +	return ret;
> > +}
> > +
> > +static void disable_sys_clock(struct isp_p1_device *p1_dev)
> > +{
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +
> > +	dev_info(dev, "- %s dev id:%d\n", __func__, dev->id);
> > +	clk_bulk_disable_unprepare(p1_dev->isp_clk.num_clks,
> > +				   p1_dev->isp_clk.clk_list);
> > +}
> > +
> > +static int mtk_isp_probe(struct platform_device *pdev)
> > +{
> > +	struct isp_p1_device *p1_dev;
> > +	struct mtk_isp_p1_ctx *isp_ctx;
> > +	struct isp_device *isp_dev;
> > +	struct device *dev = &pdev->dev;
> > +	struct resource *res;
> > +	int ret;
> > +	unsigned int i;
> > +
> > +	/* Allocate context */
> > +	p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL);
> > +	if (!p1_dev)
> > +		return -ENOMEM;
> > +
> > +	dev_set_drvdata(dev, p1_dev);
> > +	isp_ctx = &p1_dev->isp_ctx;
> > +	p1_dev->pdev = pdev;
> > +
> > +	p1_dev->isp_devs =
> > +		devm_kzalloc(dev,
> > +			     sizeof(struct isp_device) * ISP_DEV_NODE_NUM,
> > +			     GFP_KERNEL);
> > +	if (!p1_dev->isp_devs)
> > +		return -ENOMEM;
> > +
> > +	p1_dev->cam_dev =
> > +		devm_kzalloc(dev, sizeof(struct mtk_cam_dev), GFP_KERNEL);
> > +	if (!p1_dev->isp_devs)
> > +		return -ENOMEM;
> > +
> > +	/* iomap registers */
> > +	for (i = ISP_CAMSYS_CONFIG_IDX; i < ISP_DEV_NODE_NUM; i++) {
> > +		isp_dev = &p1_dev->isp_devs[i];
> > +		isp_dev->isp_hw_module = i;
> > +		isp_dev->dev = dev;
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> > +		isp_dev->regs = devm_ioremap_resource(dev, res);
> > +
> > +		dev_info(dev, "cam%u, map_addr=0x%lx\n",
> > +			 i, (unsigned long)isp_dev->regs);
> > +
> > +		if (!isp_dev->regs)
> > +			return PTR_ERR(isp_dev->regs);
> > +
> > +		/* support IRQ from ISP_CAM_A_IDX */
> > +		if (i >= ISP_CAM_A_IDX) {
> > +			/* reg & interrupts index is shifted with 1  */
> > +			isp_dev->irq = platform_get_irq(pdev, i - 1);
> > +			if (isp_dev->irq > 0) {
> > +				ret = devm_request_irq(dev, isp_dev->irq,
> > +						       isp_irq_cam,
> > +						       IRQF_SHARED,
> > +						       dev_driver_string(dev),
> > +						       (void *)isp_dev);
> > +				if (ret) {
> > +					dev_err(dev,
> > +						"req_irq fail, dev:%s irq=%d\n",
> > +						dev->of_node->name,
> > +						isp_dev->irq);
> > +					return ret;
> > +				}
> > +				dev_info(dev, "Registered irq=%d, ISR:%s\n",
> > +					 isp_dev->irq, dev_driver_string(dev));
> > +			}
> > +		}
> > +		spin_lock_init(&isp_dev->spinlock_irq);
> > +	}
> > +
> > +	p1_dev->isp_clk.num_clks = ARRAY_SIZE(mtk_isp_clks);
> > +	p1_dev->isp_clk.clk_list =
> > +		devm_kcalloc(dev,
> > +			     p1_dev->isp_clk.num_clks,
> > +			     sizeof(*p1_dev->isp_clk.clk_list),
> > +			     GFP_KERNEL);
> > +	if (!p1_dev->isp_clk.clk_list)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < p1_dev->isp_clk.num_clks; ++i)
> > +		p1_dev->isp_clk.clk_list->id = mtk_isp_clks[i];
> > +
> > +	ret = devm_clk_bulk_get(dev,
> > +				p1_dev->isp_clk.num_clks,
> > +				p1_dev->isp_clk.clk_list);
> > +	if (ret) {
> > +		dev_err(dev, "cannot get isp cam clock:%d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	/* Initialize reserved DMA memory */
> > +	ret = mtk_cam_reserved_memory_init(p1_dev);
> > +	if (ret) {
> > +		dev_err(dev, "failed to configure DMA memory\n");
> > +		return ret;
> > +	}
> > +
> > +	/* Initialize the v4l2 common part */
> > +	ret = mtk_cam_dev_init(pdev, p1_dev->cam_dev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
> > +	atomic_set(&p1_dev->isp_ctx.isp_user_cnt, 0);
> > +	pm_runtime_enable(dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_isp_remove(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct isp_p1_device *p1_dev = dev_get_drvdata(dev);
> > +
> > +	pm_runtime_disable(dev);
> > +	mtk_cam_dev_release(pdev, p1_dev->cam_dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_isp_suspend(struct device *dev)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct isp_device *isp_dev;
> > +	unsigned int reg_val;
> > +	int usercount, module;
> > +
> > +	module = p1_dev->isp_ctx.isp_hw_module;
> > +	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
> > +
> > +	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
> > +
> > +	/* If no user count, no further action */
> > +	if (!usercount)
> > +		return 0;
> > +
> > +	isp_dev = &p1_dev->isp_devs[module];
> > +	reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
> > +	if (reg_val & VFDATA_EN_BIT) {
> > +		dev_dbg(dev, "Cam:%d suspend, disable VF\n", module);
> > +		/* disable VF */
> > +		writel((reg_val & (~VFDATA_EN_BIT)),
> > +		       isp_dev->regs + REG_TG_VF_CON);
> > +		/*
> > +		 * After VF enable, The TG frame count will be reset to 0;
> > +		 */
> > +		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
> > +		writel((reg_val & (~CMOS_EN_BIT)),
> > +		       isp_dev->regs +  + REG_TG_SEN_MODE);
> > +	}
> > +
> > +	disable_sys_clock(p1_dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_isp_resume(struct device *dev)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct isp_device *isp_dev;
> > +	unsigned int reg_val;
> > +	int module, usercount;
> > +
> > +	module = p1_dev->isp_ctx.isp_hw_module;
> > +	usercount = atomic_read(&p1_dev->isp_ctx.isp_user_cnt);
> > +
> > +	dev_dbg(dev, "- %s:%d\n", __func__, usercount);
> > +
> > +	/* If no user count, no further action */
> > +	if (!usercount)
> > +		return 0;
> > +
> > +	enable_sys_clock(p1_dev);
> > +
> > +	/* V4L2 stream-on phase & restore HW stream-on status */
> > +	if (p1_dev->cam_dev->streaming) {
> > +		isp_dev = &p1_dev->isp_devs[module];
> > +		dev_dbg(dev, "Cam:%d resume,enable VF\n", module);
> > +		/* Enable CMOS */
> > +		reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE);
> > +		writel((reg_val | CMOS_EN_BIT),
> > +		       isp_dev->regs + REG_TG_SEN_MODE);
> > +		/* Enable VF */
> > +		reg_val = readl(isp_dev->regs + REG_TG_VF_CON);
> > +		writel((reg_val | VFDATA_EN_BIT),
> > +		       isp_dev->regs + REG_TG_VF_CON);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int isp_setup_scp_rproc(struct isp_p1_device *p1_dev)
> > +{
> > +	phandle rproc_phandle;
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	int ret;
> > +
> > +	p1_dev->scp_pdev = scp_get_pdev(p1_dev->pdev);
> > +	if (!p1_dev->scp_pdev) {
> > +		dev_err(dev, "Failed to get scp device\n");
> > +		return -ENODEV;
> > +	}
> > +	ret = of_property_read_u32(dev->of_node, "mediatek,scp",
> > +				   &rproc_phandle);
> > +	if (ret) {
> > +		dev_err(dev, "fail to get rproc_phandle:%d\n", ret);
> > +		return -EINVAL;
> > +	}
> > +
> > +	p1_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle);
> > +	dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n\n",
> > +		p1_dev->rproc_handle);
> > +	if (!p1_dev->rproc_handle) {
> > +		dev_err(dev, "fail to get rproc_handle\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = rproc_boot(p1_dev->rproc_handle);
> > +	if (ret < 0) {
> > +		/*
> > +		 * Return 0 if downloading firmware successfully,
> > +		 * otherwise it is failed
> > +		 */
> > +		return -ENODEV;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int isp_init_context(struct isp_p1_device *p1_dev)
> > +{
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct device *dev = &p1_dev->pdev->dev;
> > +	unsigned int i;
> > +
> > +	dev_dbg(dev, "init irq work thread\n");
> > +	if (!isp_ctx->isp_deque_thread.thread) {
> > +		mutex_init(&isp_ctx->composer_tx_lock);
> > +		init_waitqueue_head(&isp_ctx->isp_deque_thread.wq);
> > +		isp_ctx->isp_deque_thread.thread =
> > +			kthread_run(isp_deque_work, (void *)p1_dev,
> > +				    "isp_deque_work");
> > +		if (IS_ERR(isp_ctx->isp_deque_thread.thread)) {
> > +			dev_err(dev, "unable to alloc kthread\n");
> > +			isp_ctx->isp_deque_thread.thread = NULL;
> > +			return -ENOMEM;
> > +		}
> > +	}
> > +	spin_lock_init(&isp_ctx->irq_dequeue_lock);
> > +
> > +	INIT_LIST_HEAD(&isp_ctx->p1_enqueue_list.queue);
> > +	atomic_set(&isp_ctx->p1_enqueue_list.queue_cnt, 0);
> > +
> > +	for (i = 0; i < ISP_DEV_NODE_NUM; i++)
> > +		spin_lock_init(&p1_dev->isp_devs[i].spinlock_irq);
> > +
> > +	spin_lock_init(&isp_ctx->p1_enqueue_list.lock);
> > +	spin_lock_init(&isp_ctx->composer_txlist.lock);
> > +
> > +	atomic_set(&isp_ctx->irq_data_end, 0);
> > +	atomic_set(&isp_ctx->irq_data_start, 0);
> > +	return 0;
> > +}
> > +
> > +static int isp_uninit_context(struct isp_p1_device *p1_dev)
> > +{
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct mtk_isp_queue_job *framejob, *tmp_framejob;
> > +
> > +	spin_lock_irq(&isp_ctx->p1_enqueue_list.lock);
> > +	list_for_each_entry_safe(framejob, tmp_framejob,
> > +				 &isp_ctx->p1_enqueue_list.queue, list_entry) {
> > +		list_del(&framejob->list_entry);
> > +		kfree(framejob);
> > +	}
> > +	spin_unlock_irq(&isp_ctx->p1_enqueue_list.lock);
> > +
> > +	atomic_set(&isp_ctx->isp_user_cnt, 0);
> > +
> > +	if (!IS_ERR(isp_ctx->isp_deque_thread.thread)) {
> > +		kthread_stop(isp_ctx->isp_deque_thread.thread);
> > +		wake_up_interruptible(&isp_ctx->isp_deque_thread.wq);
> > +		isp_ctx->isp_deque_thread.thread = NULL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static unsigned int get_enable_dma_ports(struct mtk_cam_dev *cam_dev)
> 
> I think s/enable/enabled would be a clearer name for both the function
> and local variable.
> 

Ok, we will revised this "enabled" working in next patch.

> > +{
> > +	unsigned int enable_dma_ports, i;
> > +
> > +	/* Get the enabled meta DMA ports */
> > +	enable_dma_ports = 0;
> > +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> > +		if (cam_dev->mem2mem2_nodes[i].enabled)
> > +			enable_dma_ports |=
> > +				cam_dev->mem2mem2_nodes[i].desc.dma_port;
> > +	}
> > +	dev_dbg(&cam_dev->pdev->dev, "%s enable_dma_ports:0x%x",
> > +		__func__, enable_dma_ports);
> > +
> > +	return enable_dma_ports;
> > +}
> > +
> > +/* Utility functions */
> > +static unsigned int get_sensor_pixel_id(unsigned int fmt)
> > +{
> > +	switch (fmt) {
> > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> > +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> > +		return raw_pxl_id_b;
> > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> > +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> > +		return raw_pxl_id_gb;
> > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> > +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> > +		return raw_pxl_id_gr;
> > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> > +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> > +		return raw_pxl_id_r;
> > +	default:
> > +		return raw_pxl_id_b;
> > +	}
> > +}
> > +
> > +static unsigned int get_sensor_fmt(unsigned int fmt)
> > +{
> > +	switch (fmt) {
> > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +		return img_fmt_bayer8;
> > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +		return img_fmt_bayer10;
> > +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> > +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> > +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> > +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> > +		return img_fmt_bayer12;
> > +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> > +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> > +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> > +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> > +		return img_fmt_bayer14;
> > +	default:
> > +		return img_fmt_unknown;
> > +	}
> > +}
> > +
> > +static unsigned int get_img_fmt(unsigned int fourcc)
> > +{
> > +	switch (fourcc) {
> > +	case V4L2_PIX_FMT_MTISP_B8:
> > +		return img_fmt_bayer8;
> > +	case V4L2_PIX_FMT_MTISP_F8:
> > +		return img_fmt_fg_bayer8;
> > +	case V4L2_PIX_FMT_MTISP_B10:
> > +		return img_fmt_bayer10;
> > +	case V4L2_PIX_FMT_MTISP_F10:
> > +		return img_fmt_fg_bayer10;
> > +	case V4L2_PIX_FMT_MTISP_B12:
> > +		return img_fmt_bayer12;
> > +	case V4L2_PIX_FMT_MTISP_F12:
> > +		return img_fmt_fg_bayer12;
> > +	case V4L2_PIX_FMT_MTISP_B14:
> > +		return img_fmt_bayer14;
> > +	case V4L2_PIX_FMT_MTISP_F14:
> > +		return img_fmt_fg_bayer14;
> > +	default:
> > +		return img_fmt_unknown;
> > +	}
> > +}
> > +
> > +static unsigned int get_pixel_byte(unsigned int fourcc)
> > +{
> > +	switch (fourcc) {
> > +	case V4L2_PIX_FMT_MTISP_B8:
> > +	case V4L2_PIX_FMT_MTISP_F8:
> > +		return 8;
> > +	case V4L2_PIX_FMT_MTISP_B10:
> > +	case V4L2_PIX_FMT_MTISP_F10:
> > +		return 10;
> > +	case V4L2_PIX_FMT_MTISP_B12:
> > +	case V4L2_PIX_FMT_MTISP_F12:
> > +		return 12;
> > +	case V4L2_PIX_FMT_MTISP_B14:
> > +	case V4L2_PIX_FMT_MTISP_F14:
> > +		return 14;
> > +	case V4L2_PIX_FMT_MTISP_U8:
> > +	case V4L2_PIX_FMT_MTISP_U10:
> > +	case V4L2_PIX_FMT_MTISP_U12:
> > +	case V4L2_PIX_FMT_MTISP_U14:
> > +		return 16;
> > +	default:
> > +		return 10;
> > +	}
> > +}
> > +
> > +static void composer_deinit_done_cb(void *data)
> > +{
> > +	struct isp_p1_device *p1_dev = p1_ctx_to_dev(data);
> > +
> > +	disable_sys_clock(p1_dev);
> > +	/* Notify PM */
> > +	pm_runtime_put_sync(&p1_dev->pdev->dev);
> > +}
> > +
> > +/* ISP P1 interface functions */
> > +int mtk_isp_open(struct device *dev)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	s32 usercount = atomic_inc_return(&isp_ctx->isp_user_cnt);
> > +	int ret;
> > +
> > +	dev_dbg(dev, "%s usercount=%d\n", __func__, usercount);
> > +
> > +	if (usercount == 1) {
> > +		ret = isp_setup_scp_rproc(p1_dev);
> > +		if (ret)
> > +			goto scp_err;
> > +
> > +		/* ISP HW INIT */
> > +		isp_ctx->isp_hw_module = ISP_CAM_B_IDX;
> > +		/* Use pure RAW as default HW path */
> > +		isp_ctx->isp_raw_path = ISP_PURE_RAW_PATH;
> > +		/* Check enabled DMAs which is configured by media setup */
> > +		isp_ctx->enable_dma_ports =
> > +			get_enable_dma_ports(p1_dev->cam_dev);
> > +
> > +		if (!isp_ctx->enable_dma_ports) {
> > +			dev_dbg(dev, "No DMAs are enabled\n");
> > +			ret = -EINVAL;
> > +			goto scp_err;
> > +		}
> > +
> > +		pm_runtime_get_sync(dev);
> > +
> > +		ret = isp_init_context(p1_dev);
> > +		if (ret)
> > +			goto ctx_err;
> > +		ret = isp_composer_init(isp_ctx);
> > +		if (ret)
> > +			goto composer_err;
> > +		ret = isp_composer_hw_init(isp_ctx);
> > +		if (ret)
> > +			goto composer_err;
> > +
> > +		isp_composer_meta_config(&p1_dev->isp_ctx,
> > +					 isp_ctx->enable_dma_ports);
> > +	}
> > +
> > +	return 0;
> > +composer_err:
> > +	isp_uninit_context(p1_dev);
> > +ctx_err:
> > +	pm_runtime_put_sync(dev);
> > +scp_err:
> > +	atomic_dec_return(&isp_ctx->isp_user_cnt);
> > +	return ret;
> > +}
> > +
> > +int mtk_isp_release(struct device *dev)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +
> > +	if (atomic_dec_and_test(&p1_dev->isp_ctx.isp_user_cnt)) {
> > +		isp_composer_hw_deinit(isp_ctx, composer_deinit_done_cb);
> > +		isp_uninit_context(p1_dev);
> > +	}
> > +
> > +	dev_dbg(dev, "%s usercount=%d\n", __func__,
> > +		atomic_read(&p1_dev->isp_ctx.isp_user_cnt));
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_isp_config(struct device *dev)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct p1_config_param config_param;
> > +	struct mtk_cam_dev *cam_dev = p1_dev->cam_dev;
> > +	struct v4l2_subdev_format sd_format;
> > +	unsigned int sd_width, sd_height;
> > +	unsigned int enable_dma_ports, idx;
> > +	int ret;
> > +
> > +	p1_dev->isp_devs[isp_ctx->isp_hw_module].current_frame = 0;
> > +	p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count = 0;
> > +
> > +	isp_ctx->frame_seq_no = 1;
> > +	atomic_set(&isp_ctx->composed_frame_id, 0);
> > +
> > +	/* Get the enabled DMA ports */
> > +	enable_dma_ports = isp_ctx->enable_dma_ports;
> > +	dev_dbg(dev, "%s enable_dma_ports:0x%x", __func__, enable_dma_ports);
> > +
> > +	/* sensor config */
> > +	sd_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > +	ret = v4l2_subdev_call(cam_dev->sensor,
> > +			       pad, get_fmt, NULL, &sd_format);
> > +
> > +	if (ret) {
> > +		dev_dbg(dev, "sensor:%s g_fmt on failed:%d\n",
> > +			cam_dev->sensor->entity.name, ret);
> > +		return -EPERM;
> > +	}
> > +
> > +	dev_dbg(dev,
> > +		"sensor get_fmt ret=%d, w=%d, h=%d, code=0x%x, field=%d, color=%d\n",
> > +		ret, sd_format.format.width, sd_format.format.height,
> > +		sd_format.format.code, sd_format.format.field,
> > +		sd_format.format.colorspace);
> > +
> > +	config_param.cfg_in_param.continuous = 0x1;
> > +	config_param.cfg_in_param.subsample = 0x0;
> > +	/* fix to one pixel mode in default */
> > +	config_param.cfg_in_param.pixel_mode = one_pixel_mode;
> > +	/* support normal pattern in default */
> > +	config_param.cfg_in_param.data_pattern = 0x0;
> > +
> > +	config_param.cfg_in_param.crop.left = 0x0;
> > +	config_param.cfg_in_param.crop.top = 0x0;
> > +
> > +	config_param.cfg_in_param.raw_pixel_id =
> > +		get_sensor_pixel_id(sd_format.format.code);
> > +	config_param.cfg_in_param.img_fmt =
> > +		get_sensor_fmt(sd_format.format.code);
> > +	config_param.cfg_in_param.crop.width = sd_format.format.width;
> > +	config_param.cfg_in_param.crop.height = sd_format.format.height;
> > +	sd_width = sd_format.format.width;
> > +	sd_height = sd_format.format.height;
> > +
> > +	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
> 
> The idx variable is unnecessary. Just use MTK_CAM_P1_... to index into
> mem2mem2_nodes directly here and below.
> 

Ok, we will remove idx variable and use const value to index.

> > +	if ((enable_dma_ports & R_IMGO) == R_IMGO) {
> > +		struct v4l2_format *imgo_fmt =
> > +			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> > +
> > +		config_param.cfg_main_param.pure_raw = isp_ctx->isp_raw_path;
> > +		config_param.cfg_main_param.pure_raw_pack = 1;
> > +		config_param.cfg_main_param.bypass = 0;
> > +
> > +		config_param.cfg_main_param.output.img_fmt =
> > +			get_img_fmt(imgo_fmt->fmt.pix_mp.pixelformat);
> > +		config_param.cfg_main_param.output.pixel_byte =
> > +			get_pixel_byte(imgo_fmt->fmt.pix_mp.pixelformat);
> > +		config_param.cfg_main_param.output.size.w =
> > +			imgo_fmt->fmt.pix_mp.width;
> > +		config_param.cfg_main_param.output.size.h =
> > +			imgo_fmt->fmt.pix_mp.height;
> > +
> > +		config_param.cfg_main_param.output.size.stride =
> > +			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> > +		config_param.cfg_main_param.output.size.xsize =
> > +			imgo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> > +
> > +		config_param.cfg_main_param.output.crop.left = 0x0;
> > +		config_param.cfg_main_param.output.crop.top = 0x0;
> > +
> > +		config_param.cfg_main_param.output.crop.width = sd_width;
> > +		config_param.cfg_main_param.output.crop.height = sd_height;
> > +
> > +		WARN_ONCE(imgo_fmt->fmt.pix_mp.width > sd_width ||
> > +			  imgo_fmt->fmt.pix_mp.height > sd_height,
> > +			  "img out:%d:%d in:%d:%d",
> > +			  imgo_fmt->fmt.pix_mp.width,
> > +			  imgo_fmt->fmt.pix_mp.height,
> > +			  sd_width,
> > +			  sd_height);
> > +
> > +		dev_dbg(dev,
> > +			"imgo pixel_byte:%d img_fmt:0x%x raw:%d\n",
> > +			config_param.cfg_main_param.output.pixel_byte,
> > +			config_param.cfg_main_param.output.img_fmt,
> > +			config_param.cfg_main_param.pure_raw);
> > +		dev_dbg(dev,
> > +			"imgo param:size=%0dx%0d, stride:%d,xsize:%d,crop=%0dx%0d\n",
> > +			config_param.cfg_main_param.output.size.w,
> > +			config_param.cfg_main_param.output.size.h,
> > +			config_param.cfg_main_param.output.size.stride,
> > +			config_param.cfg_main_param.output.size.xsize,
> > +			config_param.cfg_main_param.output.crop.width,
> > +			config_param.cfg_main_param.output.crop.height);
> > +	} else {
> > +		config_param.cfg_main_param.bypass = 1;
> > +	}
> > +
> > +	idx = MTK_CAM_P1_PACKED_BIN_OUT;
> > +	if ((enable_dma_ports & R_RRZO) == R_RRZO) {
> > +		struct v4l2_format *rrzo_fmt =
> > +			&p1_dev->cam_dev->mem2mem2_nodes[idx].vdev_fmt;
> > +
> > +		config_param.cfg_resize_param.bypass = 0;
> > +		config_param.cfg_resize_param.output.img_fmt =
> > +			get_img_fmt(rrzo_fmt->fmt.pix_mp.pixelformat);
> > +		config_param.cfg_resize_param.output.pixel_byte =
> > +			get_pixel_byte(rrzo_fmt->fmt.pix_mp.pixelformat);
> > +		config_param.cfg_resize_param.output.size.w =
> > +			rrzo_fmt->fmt.pix_mp.width;
> > +		config_param.cfg_resize_param.output.size.h =
> > +			rrzo_fmt->fmt.pix_mp.height;
> > +		config_param.cfg_resize_param.output.size.stride =
> > +			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> > +		config_param.cfg_resize_param.output.size.xsize =
> > +			rrzo_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> > +
> > +		config_param.cfg_resize_param.output.crop.left = 0x0;
> > +		config_param.cfg_resize_param.output.crop.top = 0x0;
> > +		config_param.cfg_resize_param.output.crop.width = sd_width;
> > +		config_param.cfg_resize_param.output.crop.height = sd_height;
> > +
> > +		WARN_ONCE(rrzo_fmt->fmt.pix_mp.width > sd_width ||
> > +			  rrzo_fmt->fmt.pix_mp.height > sd_height,
> > +			  "rrz out:%d:%d in:%d:%d",
> > +			  rrzo_fmt->fmt.pix_mp.width,
> > +			  rrzo_fmt->fmt.pix_mp.height,
> > +			  sd_width,
> > +			  sd_height);
> > +
> > +		dev_dbg(dev, "rrzo pixel_byte:%d img_fmt:0x%x\n",
> > +			config_param.cfg_resize_param.output.pixel_byte,
> > +			config_param.cfg_resize_param.output.img_fmt);
> > +		dev_dbg(dev,
> > +			"rrzo param:size=%0dx%0d,stride:%d,xsize:%d,crop=%0dx%0d\n",
> > +			config_param.cfg_resize_param.output.size.w,
> > +			config_param.cfg_resize_param.output.size.h,
> > +			config_param.cfg_resize_param.output.size.stride,
> > +			config_param.cfg_resize_param.output.size.xsize,
> > +			config_param.cfg_resize_param.output.crop.width,
> > +			config_param.cfg_resize_param.output.crop.height);
> > +	} else {
> > +		config_param.cfg_resize_param.bypass = 1;
> > +	}
> > +
> > +	/* Configure meta DMAs info. */
> > +	config_param.cfg_meta_param.enabled_meta_dmas = enable_dma_ports;
> > +
> > +	isp_composer_hw_config(isp_ctx, &config_param);
> > +
> > +	dev_dbg(dev, "%s done\n", __func__);
> > +	return 0;
> > +}
> > +
> > +int mtk_isp_enqueue(struct device *dev,
> > +		    unsigned int dma_port,
> > +		    struct mtk_cam_dev_buffer *buffer)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct mtk_isp_scp_p1_cmd frameparams;
> > +
> > +	memset(&frameparams, 0, sizeof(frameparams));
> > +
> > +	frameparams.cmd_id = ISP_CMD_ENQUEUE_META;
> > +	frameparams.meta_frame.enabled_dma = dma_port;
> > +	frameparams.meta_frame.vb_index = buffer->vbb.vb2_buf.index;
> > +	frameparams.meta_frame.meta_addr.iova = buffer->daddr;
> > +	frameparams.meta_frame.meta_addr.scp_addr = buffer->scp_addr;
> > +
> > +	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_CMD);
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_isp_req_enqueue(struct device *dev,
> > +			struct mtk_cam_dev_start_param *frameparamsbase)
> > +{
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	struct p1_frame_param frameparams;
> > +	struct mtk_isp_queue_job *framejob;
> > +	struct mtk_cam_dev_buffer **bundle_buffers;
> > +	unsigned int i, idx;
> > +
> > +	framejob = kzalloc(sizeof(*framejob), GFP_ATOMIC);
> > +	memset(framejob, 0, sizeof(*framejob));
> > +	memset(&frameparams, 0, sizeof(frameparams));
> > +	INIT_LIST_HEAD(&framejob->list_buf);
> > +
> > +	bundle_buffers = &frameparamsbase->buffers[0];
> > +	frameparams.frame_seq_no = isp_ctx->frame_seq_no++;
> > +	frameparams.sof_idx =
> > +		p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count;
> > +	framejob->request_fd = frameparamsbase->request_fd;
> > +	framejob->frame_seq_no = frameparams.frame_seq_no;
> > +
> > +	idx = MTK_CAM_P1_META_IN_0;
> > +	if (bundle_buffers[idx]) {
> > +		frameparams.tuning_addr.iova =
> > +			bundle_buffers[idx]->daddr;
> > +		frameparams.tuning_addr.scp_addr =
> > +			bundle_buffers[idx]->scp_addr;
> > +		list_add_tail(&bundle_buffers[idx]->list,
> > +			      &framejob->list_buf);
> > +	}
> > +
> > +	/* Image output */
> > +	idx = MTK_CAM_P1_MAIN_STREAM_OUT;
> > +	if (bundle_buffers[idx]) {
> > +		frameparams.img_dma_buffers[0].buffer.iova =
> > +			bundle_buffers[idx]->daddr;
> > +		frameparams.img_dma_buffers[0].buffer.scp_addr =
> > +			bundle_buffers[idx]->scp_addr;
> > +		dev_dbg(dev, "main stream address iova:0x%x\n",
> > +			frameparams.img_dma_buffers[0].buffer.iova);
> > +		list_add_tail(&bundle_buffers[idx]->list,
> > +			      &framejob->list_buf);
> > +	}
> > +
> > +	/* Resize output */
> > +	idx = MTK_CAM_P1_PACKED_BIN_OUT;
> > +	if (bundle_buffers[idx]) {
> > +		frameparams.img_dma_buffers[1].buffer.iova =
> > +			bundle_buffers[idx]->daddr;
> > +		frameparams.img_dma_buffers[1].buffer.scp_addr =
> > +			bundle_buffers[idx]->scp_addr;
> > +		dev_dbg(dev, "packed out address iova:0x%x\n",
> > +			frameparams.img_dma_buffers[1].buffer.iova);
> > +		list_add_tail(&bundle_buffers[idx]->list,
> > +			      &framejob->list_buf);
> > +	}
> > +
> > +	/* Meta output DMAs */
> > +	for (i = 0; i < MAX_META_DMA_NODES; i++) {
> > +		idx = MTK_CAM_P1_META_OUT_0 + i;
> > +		if (bundle_buffers[idx]) {
> > +			frameparams.meta_addrs[i].iova =
> > +			  bundle_buffers[idx]->daddr;
> > +			frameparams.meta_addrs[i].scp_addr =
> > +			  bundle_buffers[idx]->scp_addr;
> > +			list_add_tail(&bundle_buffers[idx]->list,
> > +				      &framejob->list_buf);
> > +		} else {
> > +			frameparams.meta_addrs[i].iova = 0;
> > +			frameparams.meta_addrs[i].scp_addr = 0;
> > +		}
> > +	}
> > +
> > +	spin_lock(&isp_ctx->p1_enqueue_list.lock);
> > +	list_add_tail(&framejob->list_entry, &isp_ctx->p1_enqueue_list.queue);
> > +	atomic_inc(&isp_ctx->p1_enqueue_list.queue_cnt);
> > +	spin_unlock(&isp_ctx->p1_enqueue_list.lock);
> > +
> > +	isp_composer_enqueue(isp_ctx, &frameparams, SCP_ISP_FRAME);
> > +	dev_dbg(dev, "request fd:%d frame_seq_no:%d is queued cnt:%d\n",
> > +		frameparamsbase->request_fd,
> > +		frameparams.frame_seq_no,
> > +		atomic_read(&isp_ctx->p1_enqueue_list.queue_cnt));
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct dev_pm_ops mtk_isp_pm_ops = {
> > +	SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_suspend, mtk_isp_resume)
> > +	SET_RUNTIME_PM_OPS(mtk_isp_suspend, mtk_isp_resume, NULL)
> > +};
> > +
> > +static struct platform_driver mtk_isp_driver = {
> > +	.probe   = mtk_isp_probe,
> > +	.remove  = mtk_isp_remove,
> > +	.driver  = {
> > +		.name  = "mtk-cam",
> > +		.of_match_table = of_match_ptr(mtk_isp_of_ids),
> > +		.pm     = &mtk_isp_pm_ops,
> > +	}
> > +};
> > +
> > +module_platform_driver(mtk_isp_driver);
> > +
> > +MODULE_DESCRIPTION("Camera ISP driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> > new file mode 100644
> > index 000000000000..6cf8bb4ba93a
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> > @@ -0,0 +1,300 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Ryan Yu <ryan.yu@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef __CAMERA_ISP_H
> > +#define __CAMERA_ISP_H
> > +
> > +#include <linux/cdev.h>
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/ioctl.h>
> > +#include <linux/irqreturn.h>
> > +#include <linux/miscdevice.h>
> > +#include <linux/pm_qos.h>
> > +#include <linux/scatterlist.h>
> > +
> > +#include "mtk_cam-dev.h"
> > +#include "mtk_cam-scp.h"
> > +
> > +#define CAM_A_MAX_WIDTH		3328U
> > +#define CAM_A_MAX_HEIGHT	2496U
> > +#define CAM_B_MAX_WIDTH		5376U
> > +#define CAM_B_MAX_HEIGHT	4032U
> > +
> > +#define CAM_MIN_WIDTH		80U
> > +#define CAM_MIN_HEIGHT		60U
> > +
> > +#define IMG_MAX_WIDTH		CAM_B_MAX_WIDTH
> > +#define IMG_MAX_HEIGHT		CAM_B_MAX_HEIGHT
> > +#define IMG_MIN_WIDTH		CAM_MIN_WIDTH
> > +#define IMG_MIN_HEIGHT		CAM_MIN_HEIGHT
> > +
> > +#define RRZ_MAX_WIDTH		CAM_B_MAX_WIDTH
> > +#define RRZ_MAX_HEIGHT		CAM_B_MAX_HEIGHT
> > +#define RRZ_MIN_WIDTH		CAM_MIN_WIDTH
> > +#define RRZ_MIN_HEIGHT		CAM_MIN_HEIGHT
> > +
> > +#define R_IMGO		BIT(0)
> > +#define R_RRZO		BIT(1)
> > +#define R_AAO		BIT(3)
> > +#define R_AFO		BIT(4)
> > +#define R_LCSO		BIT(5)
> > +#define R_PDO		BIT(6)
> > +#define R_LMVO		BIT(7)
> > +#define R_FLKO		BIT(8)
> > +#define R_RSSO		BIT(9)
> > +#define R_PSO		BIT(10)
> > +
> > +#define ISP_COMPOSING_MAX_NUM		4
> > +#define ISP_FRAME_COMPOSING_MAX_NUM	3
> > +
> > +#define IRQ_DATA_BUF_SIZE		4
> > +#define COMPOSRE_EVENT_BUF_SIZE		4
> > +
> > +#define CQ_ADDRESS_OFFSET		0x640
> > +#define CQ_BUFFER_COUNT			3
> 
> Please align macro values using tabs.
> 
> > +
> > +#define IRQ_STAT_STR "cam%c, SOF_%d irq(0x%x), " \
> > +			"dma(0x%x), frame_num(%d)/cq_num(%d)\n"
> > +
> > +/*
> > + * In order with the sequence of device nodes defined in dtsi rule,
> > + * one hardware module should be mapping to one node.
> > + */
> > +enum isp_dev_node_enum {
> > +	ISP_CAMSYS_CONFIG_IDX = 0,
> > +	ISP_CAM_UNI_IDX,
> > +	ISP_CAM_A_IDX,
> > +	ISP_CAM_B_IDX,
> > +	ISP_DEV_NODE_NUM
> > +};
> > +
> > +/* Image RAW path for ISP P1 module. */
> > +enum isp_raw_path_enum {
> > +	ISP_PROCESS_RAW_PATH = 0,
> > +	ISP_PURE_RAW_PATH
> > +};
> > +
> > +enum {
> > +	img_fmt_unknown		= 0x0000,
> > +	img_fmt_raw_start	= 0x2200,
> > +	img_fmt_bayer8		= img_fmt_raw_start,
> > +	img_fmt_bayer10,
> > +	img_fmt_bayer12,
> > +	img_fmt_bayer14,
> > +	img_fmt_fg_bayer8,
> > +	img_fmt_fg_bayer10,
> > +	img_fmt_fg_bayer12,
> > +	img_fmt_fg_bayer14,
> > +};
> > +
> > +enum {
> > +	raw_pxl_id_b   = 0,
> > +	raw_pxl_id_gb,
> > +	raw_pxl_id_gr,
> > +	raw_pxl_id_r
> > +};
> > +
> > +enum {
> > +	default_pixel_mode = 0,
> > +	one_pixel_mode,
> > +	two_pixel_mode,
> > +	four_pixel_mode,
> > +	pixel_mode_num,
> > +};
> > +
> > +enum mtk_isp_scp_ipi_type {
> > +	SCP_ISP_CMD = 0,
> > +	SCP_ISP_FRAME,
> > +};
> > +
> > +struct isp_queue {
> > +	struct list_head queue;
> > +	atomic_t queue_cnt;
> > +	spinlock_t lock; /* queue attributes protection */
> > +};
> > +
> > +struct isp_thread {
> > +	struct task_struct *thread;
> > +	wait_queue_head_t wq;
> > +};
> > +
> > +struct mtk_isp_queue_work {
> > +	union {
> > +		struct mtk_isp_scp_p1_cmd cmd;
> > +		struct p1_frame_param frameparams;
> > +	};
> > +	struct list_head list_entry;
> > +	enum mtk_isp_scp_ipi_type type;
> > +};
> > +
> > +struct mtk_cam_dev_stat_event_data {
> > +	__u32 frame_seq_no;
> > +	__u32 meta0_vb2_index;
> > +	__u32 meta1_vb2_index;
> > +	__u32 irq_status_mask;
> > +	__u32 dma_status_mask;
> > +};
> > +
> > +struct mtk_isp_queue_job {
> > +	struct list_head list_entry;
> > +	struct list_head list_buf;
> > +	unsigned int request_fd;
> > +	unsigned int frame_seq_no;
> > +};
> > +
> > +struct isp_clk_struct {
> > +	int num_clks;
> > +	struct clk_bulk_data *clk_list;
> > +};
> > +
> > +struct isp_device {
> > +	struct device *dev;
> > +	void __iomem *regs;
> > +	int irq;
> > +	spinlock_t spinlock_irq; /* ISP reg setting integrity */
> > +	unsigned int current_frame;
> > +	unsigned int meta0_vb2_index;
> > +	unsigned int meta1_vb2_index;
> > +	u8 sof_count;
> > +	u8 isp_hw_module;
> > +};
> > +
> > +struct mtk_isp_p1_ctx {
> > +	struct isp_queue composer_txlist;
> > +	struct isp_thread composer_tx_thread;
> > +	atomic_t cmd_queued;
> > +	struct mutex composer_tx_lock; /* isp composer work protection */
> > +
> > +	struct isp_thread composer_rx_thread;
> > +	struct mtk_isp_scp_p1_cmd composer_evts[COMPOSRE_EVENT_BUF_SIZE];
> > +	atomic_t composer_evts_start;
> > +	atomic_t composer_evts_end;
> > +	spinlock_t composer_evts_lock; /* SCP events protection */
> > +	/* increase after ipi */
> > +	atomic_t ipi_occupied;
> > +	/* increase after frame enqueue */
> > +	atomic_t composing_frame;
> > +	/* current composed frame id */
> > +	atomic_t composed_frame_id;
> > +
> > +	struct isp_queue p1_enqueue_list;
> > +
> > +	struct isp_thread isp_deque_thread;
> > +	struct mtk_cam_dev_stat_event_data irq_event_datas[IRQ_DATA_BUF_SIZE];
> > +	atomic_t irq_data_start;
> > +	atomic_t irq_data_end;
> > +	spinlock_t irq_dequeue_lock; /* ISP frame dequeuq protection */
> > +
> > +	dma_addr_t scp_mem_pa;
> > +	dma_addr_t scp_mem_iova;
> > +	struct sg_table sgtable;
> > +
> > +	/* increase after open, decrease when close */
> > +	atomic_t isp_user_cnt;
> > +	/* frame sequence number, increase per en-queue*/
> > +	int frame_seq_no;
> > +	unsigned int isp_hw_module;
> > +	unsigned int isp_raw_path;
> > +	unsigned int enable_dma_ports;
> > +
> > +	void (*composer_deinit_donecb)(void *isp_ctx);
> > +
> > +	struct list_head list;
> > +};
> > +
> > +struct isp_p1_device {
> > +	struct platform_device *pdev;
> > +
> > +	/* for SCP driver  */
> > +	struct platform_device *scp_pdev;
> > +	struct rproc *rproc_handle;
> > +
> > +	struct mtk_isp_p1_ctx isp_ctx;
> > +	struct isp_clk_struct isp_clk;
> 
> What's the benefit of having mtk_isp_p1_ctx and isp_clk_struct in
> separate structs? They are only ever used in isp_p1_device.
> 

Ok, we will merge isp_clk_struct into mtk_isp_p1_ctx in next patch.

> > +	struct mtk_cam_dev *cam_dev;
> > +	struct isp_device *isp_devs;
> 
> Similarly, why are these allocated at runtime rather then just members
> of the struct?
> 

Ok, we will revise new isp_p1_device structure as below list:

struct isp_p1_device {
	struct platform_device *pdev;
	struct platform_device *scp_pdev;
	struct rproc *rproc_handle;
	struct mtk_isp_p1_ctx isp_ctx;
	struct mtk_cam_dev cam_dev;
	struct isp_device isp_devs[ISP_DEV_NODE_NUM];
};


> In mtk_isp_probe the struct isp_p1_device is allocated, and immediately
> afterwards the struct mtk_cam_dev and struct isp_devices are. There will
> only ever be ISP_DEV_NODE_NUM isp_devices.
> 
> Could this be changed to:
> 	struct mtk_cam_dev cam_dev;
> 	struct isp_device isp_devs[ISP_DEV_NODE_NUM];
> ?
> 

Yes, thanks for your suggestion as above.

> > +};
> > +
> > +static inline struct isp_p1_device *
> > +p1_ctx_to_dev(const struct mtk_isp_p1_ctx *__p1_ctx)
> > +{
> > +	return container_of(__p1_ctx, struct isp_p1_device, isp_ctx);
> > +}
> > +
> > +static inline struct isp_p1_device *get_p1_device(struct device *dev)
> > +{
> > +	return ((struct isp_p1_device *)dev_get_drvdata(dev));
> > +}
> > +
> > +int isp_composer_init(struct mtk_isp_p1_ctx *isp_ctx);
> > +int isp_composer_hw_init(struct mtk_isp_p1_ctx *isp_ctx);
> > +void isp_composer_meta_config(struct mtk_isp_p1_ctx *isp_ctx,
> > +			      unsigned int dma);
> > +void isp_composer_hw_config(struct mtk_isp_p1_ctx *isp_ctx,
> > +			    struct p1_config_param *config_param);
> > +void isp_composer_stream(struct mtk_isp_p1_ctx *isp_ctx, int on);
> > +void isp_composer_hw_deinit(struct mtk_isp_p1_ctx *isp_ctx,
> > +			    void (*donecb)(void *data));
> > +void isp_composer_enqueue(struct mtk_isp_p1_ctx *isp_ctx,
> > +			  void *data,
> > +			  enum mtk_isp_scp_ipi_type type);
> 
> These functions are declared here, but implemented in mtk_cam-scp.c.
> Can the funtion declarations be moved to mtk_cam-scp.h?
> 

Ok, we will revise these function declared in next patch.

Best regards,


Jungo 

> > +
> > +/**
> > + * mtk_isp_open - open isp driver and initialize related resources.
> > + *
> > + * @dev:	isp device.
> > + *
> > + */
> > +int mtk_isp_open(struct device *dev);
> > +
> > +/**
> > + * mtk_isp_release - release isp driver and related resources.
> > + *
> > + * @dev:	isp device.
> > + *
> > + */
> > +int mtk_isp_release(struct device *dev);
> > +
> > +/**
> > + * mtk_isp_config - output image & meta data configuration.
> > + *
> > + * @dev:	isp device.
> > + *
> > + */
> > +int mtk_isp_config(struct device *dev);
> > +
> > +/**
> > + * mtk_isp_req_enqueue - enqueue a frame bundle (per-frame basis) to ISP driver.
> > + *
> > + * @dev:	isp device.
> > + * @frameparamsbase: pointer to &struct mtk_cam_dev_start_param.
> > + *
> > + */
> > +int mtk_isp_req_enqueue(struct device *dev,
> > +			struct mtk_cam_dev_start_param *frameparamsbase);
> > +
> > +/**
> > + * mtk_isp_enqueue - enqueue a single frame to ISP driver
> > + * for non-per-frame DMA.
> > + *
> > + * @dev:	isp device.
> > + * @buffer: pointer to &struct mtk_cam_dev_buffer.
> > + *
> > + */
> > +int mtk_isp_enqueue(struct device *dev,
> > +		    unsigned int dma_idx,
> > +		    struct mtk_cam_dev_buffer *buffer);
> > +#endif /*__CAMERA_ISP_H*/
> > -- 
> > 2.18.0
> > 
> 
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek



_______________________________________________
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] 104+ messages in thread

* Re: [RFC,V2,08/11] media: platform: Add Mediatek ISP P1 V4L2 functions
  2019-05-24 18:49   ` Drew Davenport
@ 2019-05-28  1:00     ` Jungo Lin
  0 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-05-28  1:00 UTC (permalink / raw)
  To: Drew Davenport
  Cc: ryan.yu, frankie.chiu, laurent.pinchart+renesas, Rynn.Wu,
	suleiman, Jerry-ch.Chen, hans.verkuil, frederic.chen,
	seraph.huang, linux-media, devicetree, shik, yuzhao,
	linux-mediatek, matthias.bgg, mchehab, linux-arm-kernel,
	Sean.Cheng, srv_heupstream, sj.huang, tfiga, christie.yu,
	zwisler

Hi, Drew:

Appreciate your feedbacks on this patch set firstly.

On Fri, 2019-05-24 at 12:49 -0600, Drew Davenport wrote:
> Hi Jungo,
> 
> On Fri, May 10, 2019 at 09:58:02AM +0800, Jungo Lin wrote:
> > Implement standard V4L2 video driver that utilizes V4L2
> > and media framework APIs. In this driver, supports one media
> > device, one sub-device and six video devices during
> > initialization. Moreover, it also connects with sensor and
> > senif drivers with V4L2 async APIs.
> 
> Thanks for the patch. I've made a few comments inline. As a general
> comment, what do you think of merging mtk_cam-dev.c and
> mtk_cam-v4l2-util.c into one file? They seem to call into one another
> and I'm not sure how beneficial it is to have them separate.
> 
> I have some comments on the other patches in this series that came about
> while I was reviewing this, which I will send as well.
> 
> [snip]
>  

Ok, we will merge tk_cam-dev.c into mtk_cam-v4l2-util.c in next patch
set.

> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> > new file mode 100644
> > index 000000000000..5a581ab65945
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> > @@ -0,0 +1,19 @@
> > +#
> > +# Copyright (C) 2018 MediaTek Inc.
> > +#
> > +# This program is free software: you can redistribute it and/or modify
> > +# it under the terms of the GNU General Public License version 2 as
> > +# published by the Free Software Foundation.
> > +#
> > +# This program is distributed in the hope that it will be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > +# GNU General Public License for more details.
> > +#
> > +
> > +mtk-cam-isp-objs += \
> > +	mtk_cam.o mtk_cam-dev.o \
> > +	mtk_cam-ctrl.o mtk_cam-scp.o \
> > +	mtk_cam-v4l2-util.o mtk_cam-smem-dev.o
> 
> Some of these files are added in other patches. Consider adding files to
> the Makefile in the same patch a file is added.
> 

Ok, we will add the corresponding files in each patches.

> > +
> > +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1_SUPPORT) += mtk-cam-isp.o
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
> > new file mode 100644
> > index 000000000000..dda8a7b161ee
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.c
> > @@ -0,0 +1,758 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018 Mediatek Corporation.
> > + * Copyright (c) 2017 Intel Corporation.
> > + * Copyright (C) 2017 Google, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License version
> > + * 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/device.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/videodev2.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +
> > +#include "mtk_cam.h"
> > +#include "mtk_cam-dev.h"
> > +#include "mtk_cam-smem.h"
> > +#include "mtk_cam-v4l2-util.h"
> > +
> > +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
> > +	.vidioc_querycap = mtk_cam_videoc_querycap,
> > +	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
> > +	.vidioc_enum_fmt_vid_cap_mplane = mtk_cam_videoc_enum_fmt,
> > +	.vidioc_g_fmt_vid_cap_mplane = mtk_cam_videoc_g_fmt,
> > +	.vidioc_s_fmt_vid_cap_mplane = mtk_cam_videoc_s_fmt,
> > +	.vidioc_try_fmt_vid_cap_mplane = mtk_cam_videoc_try_fmt,
> > +	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
> > +	.vidioc_g_input = mtk_cam_vidioc_g_input,
> > +	.vidioc_s_input = mtk_cam_vidioc_s_input,
> > +	/* buffer queue management */
> > +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> > +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> > +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> > +	.vidioc_querybuf = vb2_ioctl_querybuf,
> > +	.vidioc_qbuf = vb2_ioctl_qbuf,
> > +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> > +	.vidioc_streamon = vb2_ioctl_streamon,
> > +	.vidioc_streamoff = vb2_ioctl_streamoff,
> > +	.vidioc_expbuf = vb2_ioctl_expbuf,
> > +	.vidioc_subscribe_event = mtk_cam_vidioc_subscribe_event,
> > +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vout_ioctl_ops = {
> 
> This is not used anywhere. Please remove.
> 

Ok, we will remove unused variable.

> > +	.vidioc_querycap = mtk_cam_videoc_querycap,
> > +	.vidioc_enum_framesizes = mtk_cam_enum_framesizes,
> > +	.vidioc_enum_fmt_vid_out_mplane = mtk_cam_videoc_enum_fmt,
> > +	.vidioc_g_fmt_vid_out_mplane = mtk_cam_videoc_g_fmt,
> > +	.vidioc_s_fmt_vid_out_mplane = mtk_cam_videoc_s_fmt,
> > +	.vidioc_try_fmt_vid_out_mplane = mtk_cam_videoc_try_fmt,
> > +	.vidioc_enum_input = mtk_cam_vidioc_enum_input,
> > +	.vidioc_g_input = mtk_cam_vidioc_g_input,
> > +	.vidioc_s_input = mtk_cam_vidioc_s_input,
> > +	/* buffer queue management */
> > +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> > +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> > +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> > +	.vidioc_querybuf = vb2_ioctl_querybuf,
> > +	.vidioc_qbuf = vb2_ioctl_qbuf,
> > +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> > +	.vidioc_streamon = vb2_ioctl_streamon,
> > +	.vidioc_streamoff = vb2_ioctl_streamoff,
> > +	.vidioc_expbuf = vb2_ioctl_expbuf,
> > +};
> > +
> > +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
> > +	.vidioc_querycap = mtk_cam_videoc_querycap,
> > +	.vidioc_enum_fmt_meta_cap = mtk_cam_meta_enum_format,
> > +	.vidioc_g_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_s_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_try_fmt_meta_cap = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> > +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> > +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> > +	.vidioc_querybuf = vb2_ioctl_querybuf,
> > +	.vidioc_qbuf = vb2_ioctl_qbuf,
> > +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> > +	.vidioc_streamon = vb2_ioctl_streamon,
> > +	.vidioc_streamoff = vb2_ioctl_streamoff,
> > +	.vidioc_expbuf = vb2_ioctl_expbuf,
> > +};
> > +
> > +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
> > +	.vidioc_querycap = mtk_cam_videoc_querycap,
> > +	.vidioc_enum_fmt_meta_out = mtk_cam_meta_enum_format,
> > +	.vidioc_g_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_s_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_try_fmt_meta_out = mtk_cam_videoc_g_meta_fmt,
> > +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> > +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> > +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> > +	.vidioc_querybuf = vb2_ioctl_querybuf,
> > +	.vidioc_qbuf = vb2_ioctl_qbuf,
> > +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> > +	.vidioc_streamon = vb2_ioctl_streamon,
> > +	.vidioc_streamoff = vb2_ioctl_streamoff,
> > +	.vidioc_expbuf = vb2_ioctl_expbuf,
> > +};
> > +
> > +static struct v4l2_format meta_fmts[] = {
> > +	{
> > +		.fmt.meta = {
> > +			.dataformat = V4L2_META_FMT_MTISP_PARAMS,
> > +			.buffersize = 128 * PAGE_SIZE,
> > +		},
> > +	},
> > +	{
> > +		.fmt.meta = {
> > +			.dataformat = V4L2_META_FMT_MTISP_3A,
> > +			.buffersize = 300 * PAGE_SIZE,
> > +		},
> > +	},
> > +	{
> > +		.fmt.meta = {
> > +			.dataformat = V4L2_META_FMT_MTISP_AF,
> > +			.buffersize = 160 * PAGE_SIZE,
> > +		},
> > +	},
> > +	{
> > +		.fmt.meta = {
> > +			.dataformat = V4L2_META_FMT_MTISP_LCS,
> > +			.buffersize = 72 * PAGE_SIZE,
> > +		},
> > +	},
> > +	{
> > +		.fmt.meta = {
> > +			.dataformat = V4L2_META_FMT_MTISP_LMV,
> > +			.buffersize = 256,
> > +		},
> > +	},
> > +};
> > +
> > +/* Need to update mtk_cam_dev_fmt_set_img for default format configuration */
> > +static struct v4l2_format stream_out_fmts[] = {
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = IMG_MAX_WIDTH,
> > +			.height = IMG_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_B8,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_SRGB,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = IMG_MAX_WIDTH,
> > +			.height = IMG_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_B10,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_SRGB,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = IMG_MAX_WIDTH,
> > +			.height = IMG_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_B12,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_SRGB,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = IMG_MAX_WIDTH,
> > +			.height = IMG_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_B14,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_SRGB,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +};
> > +
> > +static struct v4l2_format bin_out_fmts[] = {
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = RRZ_MAX_WIDTH,
> > +			.height = RRZ_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_F8,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_RAW,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = RRZ_MAX_WIDTH,
> > +			.height = RRZ_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_F10,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_RAW,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = RRZ_MAX_WIDTH,
> > +			.height = RRZ_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_F12,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_RAW,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +	{
> > +		.fmt.pix_mp = {
> > +			.width = RRZ_MAX_WIDTH,
> > +			.height = RRZ_MAX_HEIGHT,
> > +			.pixelformat = V4L2_PIX_FMT_MTISP_F14,
> > +			.field = V4L2_FIELD_NONE,
> > +			.colorspace = V4L2_COLORSPACE_RAW,
> > +			.num_planes = 1,
> > +		},
> > +	},
> > +};
> > +
> > +static const struct
> > +mtk_cam_dev_node_desc output_queues[MTK_CAM_P1_TOTAL_OUTPUT] = {
> > +	{
> > +		.id = MTK_CAM_P1_META_IN_0,
> > +		.name = "meta input",
> > +		.description = "ISP tuning parameters",
> > +		.cap = V4L2_CAP_META_OUTPUT,
> > +		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
> > +		.link_flags = 0,
> > +		.capture = false,
> > +		.image = false,
> > +		.smem_alloc = true,
> > +		.fmts = meta_fmts,
> > +		.num_fmts = ARRAY_SIZE(meta_fmts),
> > +		.default_fmt_idx = 0,
> > +		.max_buf_count = 10,
> > +		.ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
> > +	},
> > +};
> > +
> > +static const struct
> > +mtk_cam_dev_node_desc capture_queues[MTK_CAM_P1_TOTAL_CAPTURE] = {
> > +	{
> > +		.id = MTK_CAM_P1_MAIN_STREAM_OUT,
> > +		.name = "main stream",
> > +		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> > +		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> > +		.link_flags = 0,
> > +		.capture = true,
> > +		.image = true,
> > +		.smem_alloc = false,
> > +		.dma_port = R_IMGO,
> > +		.fmts = stream_out_fmts,
> > +		.num_fmts = ARRAY_SIZE(stream_out_fmts),
> > +		.default_fmt_idx = 0,
> > +		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> > +	},
> > +	{
> > +		.id = MTK_CAM_P1_PACKED_BIN_OUT,
> > +		.name = "packed out",
> > +		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> > +		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> > +		.link_flags = 0,
> > +		.capture = true,
> > +		.image = true,
> > +		.smem_alloc = false,
> > +		.dma_port = R_RRZO,
> > +		.fmts = bin_out_fmts,
> > +		.num_fmts = ARRAY_SIZE(bin_out_fmts),
> > +		.default_fmt_idx = 1,
> > +		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> > +	},
> > +	{
> > +		.id = MTK_CAM_P1_META_OUT_0,
> > +		.name = "partial meta 0",
> > +		.description = "AE/AWB histogram",
> > +		.cap = V4L2_CAP_META_CAPTURE,
> > +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> > +		.link_flags = 0,
> > +		.capture = true,
> > +		.image = false,
> > +		.smem_alloc = false,
> > +		.dma_port = R_AAO | R_FLKO | R_PSO,
> > +		.fmts = meta_fmts,
> > +		.num_fmts = ARRAY_SIZE(meta_fmts),
> > +		.default_fmt_idx = 1,
> > +		.max_buf_count = 5,
> > +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> > +	},
> > +	{
> > +		.id = MTK_CAM_P1_META_OUT_1,
> > +		.name = "partial meta 1",
> > +		.description = "AF histogram",
> > +		.cap = V4L2_CAP_META_CAPTURE,
> > +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> > +		.link_flags = 0,
> > +		.capture = true,
> > +		.image = false,
> > +		.smem_alloc = false,
> > +		.dma_port = R_AFO,
> > +		.fmts = meta_fmts,
> > +		.num_fmts = ARRAY_SIZE(meta_fmts),
> > +		.default_fmt_idx = 2,
> > +		.max_buf_count = 5,
> > +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> > +	},
> > +	{
> > +		.id = MTK_CAM_P1_META_OUT_2,
> > +		.name = "partial meta 2",
> > +		.description = "Local contrast enhanced statistics",
> > +		.cap = V4L2_CAP_META_CAPTURE,
> > +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> > +		.link_flags = MEDIA_LNK_FL_DYNAMIC,
> > +		.capture = true,
> > +		.image = false,
> > +		.smem_alloc = false,
> > +		.dma_port = R_LCSO,
> > +		.fmts = meta_fmts,
> > +		.num_fmts = ARRAY_SIZE(meta_fmts),
> > +		.default_fmt_idx = 3,
> > +		.max_buf_count = 10,
> > +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> > +	},
> > +	{
> > +		.id = MTK_CAM_P1_META_OUT_3,
> > +		.name = "partial meta 3",
> > +		.description = "Local motion vector histogram",
> > +		.cap = V4L2_CAP_META_CAPTURE,
> > +		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> > +		.link_flags = MEDIA_LNK_FL_DYNAMIC,
> > +		.capture = true,
> > +		.image = false,
> > +		.smem_alloc = false,
> > +		.dma_port = R_LMVO,
> > +		.fmts = meta_fmts,
> > +		.num_fmts = ARRAY_SIZE(meta_fmts),
> > +		.default_fmt_idx = 4,
> > +		.max_buf_count = 10,
> > +		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> > +	},
> > +};
> > +
> > +static const struct mtk_cam_dev_queues_setting queues_setting = {
> > +	.output_node_descs = output_queues,
> > +	.total_output_nodes = MTK_CAM_P1_TOTAL_OUTPUT,
> > +	.capture_node_descs = capture_queues,
> > +	.total_capture_nodes = MTK_CAM_P1_TOTAL_CAPTURE,
> > +};
> 
> I think this struct can be removed. See my comment in mtk_cam_dev_queue_setup
> 

Ok, we will remove mtk_cam_dev_queue_setup structure and revise
mtk_cam_dev_queue_setup function.

> > +
> > +static __u32 get_pixel_byte_by_fmt(__u32 pix_fmt)
> > +{
> > +	switch (pix_fmt) {
> > +	case V4L2_PIX_FMT_MTISP_B8:
> > +	case V4L2_PIX_FMT_MTISP_F8:
> > +		return 8;
> > +	case V4L2_PIX_FMT_MTISP_B10:
> > +	case V4L2_PIX_FMT_MTISP_F10:
> > +		return 10;
> > +	case V4L2_PIX_FMT_MTISP_B12:
> > +	case V4L2_PIX_FMT_MTISP_F12:
> > +		return 12;
> > +	case V4L2_PIX_FMT_MTISP_B14:
> > +	case V4L2_PIX_FMT_MTISP_F14:
> > +		return 14;
> > +	case V4L2_PIX_FMT_MTISP_U8:
> > +	case V4L2_PIX_FMT_MTISP_U10:
> > +	case V4L2_PIX_FMT_MTISP_U12:
> > +	case V4L2_PIX_FMT_MTISP_U14:
> > +		return 16;
> > +	default:
> > +		return 0;
> > +	}
> > +}
> > +
> > +static __u32 align_main_stream_size(__u32 size, unsigned int pix_mode)
> 
> Since only one_pixel_mode is supported, this function can be removed and
> the callsite replaced with ALIGN(size, 2). This function can be added
> once more when other pixel modes are supported.
> 

Got it, we will align_main_stream_size & align_packetd_out_size
functions and remove pix_mode argument in cal_main_stream_stride &
functions.

> > +{
> > +	switch (pix_mode) {
> > +	case default_pixel_mode:
> > +	case four_pixel_mode:
> > +		return ALIGN(size, 8);
> > +	case two_pixel_mode:
> > +		return ALIGN(size, 4);
> > +	case one_pixel_mode:
> > +		return ALIGN(size, 2);
> > +	default:
> > +		break;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static unsigned int align_packetd_out_size(__u32 size,
> > +					   unsigned int pix_mode,
> > +					   __u32 fmt)
> 
> This is only ever called with one_pixel_mode. Remove the pix_mode
> argument and unreachable code.
> 

Ditto.

> > +{
> > +	switch (pix_mode) {
> > +	case default_pixel_mode:
> > +	case four_pixel_mode:
> > +		return ALIGN(size, 16);
> > +	case two_pixel_mode:
> > +		return ALIGN(size, 8);
> > +	case one_pixel_mode:
> > +		if (fmt == V4L2_PIX_FMT_MTISP_F10)
> > +			return ALIGN(size, 4);
> > +		else
> > +			return ALIGN(size, 8);
> > +	default:
> > +		return ALIGN(size, 16);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static __u32 cal_main_stream_stride(struct device *dev,
> > +				    __u32 width,
> > +				    __u32 pix_fmt,
> > +				    __u32 pix_mode)
> 
> This function is only called with one_pixel_mode. Remove the pix_mode
> argument.
> 

Ditto.

> > +{
> > +	__u32 stride;
> > +	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
> > +
> > +	width = ALIGN(width, 4);
> > +	stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2);
> > +	/* expand stride, instead of shrink width */
> > +	stride = align_main_stream_size(stride, pix_mode);
> > +
> > +	dev_dbg(dev,
> > +		"main width:%d, pix_mode:%d, stride:%d\n",
> > +		width, pix_mode, stride);
> > +	return stride;
> > +}
> > +
> > +static __u32 cal_packed_out_stride(struct device *dev,
> > +				   __u32 width,
> > +				   __u32 pix_fmt,
> > +				   __u32 pix_mode)
> 
> This function is only called with one_pixel_mode. Remove the pix_mode
> argument.
> 

Ditto.

> > +{
> > +	__u32 stride;
> > +	__u32 pixel_byte = get_pixel_byte_by_fmt(pix_fmt);
> > +
> > +	width = ALIGN(width, 4);
> > +	stride = DIV_ROUND_UP(width * 3, 2);
> > +	stride = DIV_ROUND_UP(stride * pixel_byte, 8);
> > +	/* expand stride, instead of shrink width */
> > +	stride = align_packetd_out_size(stride, pix_mode, pix_fmt);
> > +
> > +	dev_dbg(dev,
> > +		"packed width:%d, pix_mode:%d, stride:%d\n",
> > +		width, pix_mode, stride);
> > +
> > +	return stride;
> > +}
> > +
> > +static __u32 cal_img_stride(struct device *dev,
> > +			    int node_id,
> > +			    __u32 width,
> > +			    __u32 pix_fmt)
> > +{
> > +	__u32 bpl;
> > +
> > +	/* Currently, only support one_pixel_mode */
> > +	if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT)
> > +		bpl = cal_main_stream_stride(dev, width, pix_fmt,
> > +					     one_pixel_mode);
> > +	else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT)
> > +		bpl = cal_packed_out_stride(dev, width, pix_fmt,
> > +					    one_pixel_mode);
> > +
> > +	/* For DIP HW constrained, it needs 4 byte alignment */
> > +	bpl = ALIGN(bpl, 4);
> > +
> > +	return bpl;
> > +}
> > +
> > +struct v4l2_format *
> > +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
> > +{
> > +	unsigned int i;
> > +	struct v4l2_format *dev_fmt;
> > +
> > +	for (i = 0; i < desc->num_fmts; i++) {
> > +		dev_fmt = &desc->fmts[i];
> > +		if (dev_fmt->fmt.pix_mp.pixelformat == format)
> > +			return dev_fmt;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +/* The helper to configure the device context */
> > +void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
> > +			     const struct mtk_cam_dev_queues_setting *setting)
> 
> This is only ever called with the same mtk_cam_dev_queues_setting
> struct. I think you can remove that struct altogether and just set the
> mtk_cam_dev_node_desc* for each node from output_queues and
> capture_queues directly.
> 
> Also this can be a static function.
> 

Thanks for your suggestion.
We will revise in next patch as below.

/* The helper to configure the device context */
static void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev)

> > +{
> > +	unsigned int i, node_idx;
> > +
> > +	node_idx = 0;
> > +
> > +	/* Setup the output queue */
> > +	for (i = 0; i < setting->total_output_nodes; i++)
> > +		cam_dev->mem2mem2_nodes[node_idx++].desc =
> > +			setting->output_node_descs[i];
> > +
> > +	/* Setup the capture queue */
> > +	for (i = 0; i < setting->total_capture_nodes; i++)
> > +		cam_dev->mem2mem2_nodes[node_idx++].desc =
> > +			setting->capture_node_descs[i];
> > +
> > +	cam_dev->dev_node_num = node_idx;
> 
> This value is known at compile time (MTK_CAM_P1_TOTAL_OUTPUT +
> MTK_CAM_P1_TOTAL_CAPTURE). Can we just #define that constant and use
> that instead of dev_node_num?
> 

Ok, we will use new MTK_CAM_P1_TOTAL_NODES const value to replace
dev_node_num in next patch.

> > +}
> > +
> > +int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
> > +			   struct mtk_cam_dev_finish_param *fram_param)
> > +{
> > +	struct mtk_cam_dev_buffer *buf, *b0;
> > +
> > +	if (!cam_dev->streaming)
> > +		return 0;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev,
> > +		"job recvied request fd:%d, frame_seq:%d state:%d\n",
> > +		fram_param->request_fd,
> > +		fram_param->frame_seq_no,
> > +		fram_param->state);
> > +
> > +	/*
> > +	 * Set the buffer's VB2 status so that the user can dequeue
> > +	 * the buffer.
> > +	 */
> > +	list_for_each_entry_safe(buf, b0, fram_param->list_buf, list) {
> > +		list_del(&buf->list);
> > +		buf->vbb.vb2_buf.timestamp = ktime_get_ns();
> > +		buf->vbb.sequence = fram_param->frame_seq_no;
> > +		if (buf->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
> > +			vb2_buffer_done(&buf->vbb.vb2_buf, fram_param->state);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
> > +				 __u32 frame_seq_no)
> > +{
> > +	struct v4l2_event event;
> > +
> > +	memset(&event, 0, sizeof(event));
> > +	event.type = V4L2_EVENT_FRAME_SYNC;
> > +	event.u.frame_sync.frame_sequence = frame_seq_no;
> > +	v4l2_event_queue(cam_dev->subdev.devnode, &event);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Calcuate mplane pix format */
> > +void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
> > +				    struct v4l2_pix_format_mplane *dest_fmt,
> > +				    unsigned int node_id)
> > +{
> > +	unsigned int i;
> > +	__u32 bpl, sizeimage, imagsize;
> > +
> > +	imagsize = 0;
> > +	for (i = 0 ; i < dest_fmt->num_planes; ++i) {
> > +		bpl = cal_img_stride(dev,
> > +				     node_id,
> > +				     dest_fmt->width,
> > +				     dest_fmt->pixelformat);
> > +		sizeimage = bpl * dest_fmt->height;
> > +		imagsize += sizeimage;
> > +		dest_fmt->plane_fmt[i].bytesperline = bpl;
> > +		dest_fmt->plane_fmt[i].sizeimage = sizeimage;
> > +		memset(dest_fmt->plane_fmt[i].reserved,
> > +		       0, sizeof(dest_fmt->plane_fmt[i].reserved));
> > +		dev_dbg(dev, "plane:%d,bpl:%d,sizeimage:%u\n",
> > +			i,  bpl, dest_fmt->plane_fmt[i].sizeimage);
> > +	}
> > +
> > +	if (dest_fmt->num_planes == 1)
> > +		dest_fmt->plane_fmt[0].sizeimage = imagsize;
> > +}
> > +
> > +void mtk_cam_dev_fmt_set_img(struct device *dev,
> > +			     struct v4l2_pix_format_mplane *dest_fmt,
> > +			     struct v4l2_pix_format_mplane *src_fmt,
> > +			     unsigned int node_id)
> > +{
> > +	dest_fmt->width = src_fmt->width;
> > +	dest_fmt->height = src_fmt->height;
> > +	dest_fmt->pixelformat = src_fmt->pixelformat;
> > +	dest_fmt->field = src_fmt->field;
> > +	dest_fmt->colorspace = src_fmt->colorspace;
> > +	dest_fmt->num_planes = src_fmt->num_planes;
> > +	/* Use default */
> > +	dest_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> > +	dest_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> > +	dest_fmt->xfer_func =
> > +		V4L2_MAP_XFER_FUNC_DEFAULT(dest_fmt->colorspace);
> > +	memset(dest_fmt->reserved, 0, sizeof(dest_fmt->reserved));
> > +
> > +	dev_dbg(dev, "%s: Dest Fmt:%c%c%c%c, w*h:%d*%d\n",
> > +		__func__,
> > +		(dest_fmt->pixelformat & 0xFF),
> > +		(dest_fmt->pixelformat >> 8) & 0xFF,
> > +		(dest_fmt->pixelformat >> 16) & 0xFF,
> > +		(dest_fmt->pixelformat >> 24) & 0xFF,
> > +		dest_fmt->width,
> > +		dest_fmt->height);
> > +
> > +	mtk_cam_dev_cal_mplane_pix_fmt(dev, dest_fmt, node_id);
> > +}
> > +
> > +/* Get the default format setting */
> > +void mtk_cam_dev_load_default_fmt(struct device *dev,
> > +				  struct mtk_cam_dev_node_desc *queue_desc,
> > +				  struct v4l2_format *dest)
> > +{
> > +	struct v4l2_format *default_fmt =
> > +		&queue_desc->fmts[queue_desc->default_fmt_idx];
> > +
> > +	dest->type = queue_desc->buf_type;
> > +
> > +	/* Configure default format based on node type */
> > +	if (queue_desc->image) {
> > +		mtk_cam_dev_fmt_set_img(dev,
> > +					&dest->fmt.pix_mp,
> > +					&default_fmt->fmt.pix_mp,
> > +					queue_desc->id);
> > +	} else {
> > +		dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
> > +		dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
> > +	}
> > +}
> > +
> > +/* Get a free buffer from a video node */
> > +static struct mtk_cam_dev_buffer *
> > +mtk_cam_dev_get_pending_buf(struct mtk_cam_dev *cam_dev, int node)
> > +{
> > +	struct mtk_cam_dev_buffer *buf;
> > +	struct mtk_cam_video_device *vdev;
> > +
> > +	if (node > cam_dev->dev_node_num || node < 0) {
> > +		dev_err(&cam_dev->pdev->dev, "Invalid mtk_cam_dev node.\n");
> > +		return NULL;
> > +	}
> > +	vdev = &cam_dev->mem2mem2_nodes[node];
> > +
> > +	spin_lock(&vdev->slock);
> > +	buf = list_first_entry_or_null(&vdev->pending_list,
> > +				       struct mtk_cam_dev_buffer,
> > +				       list);
> > +	if (!buf) {
> > +		spin_unlock(&vdev->slock);
> > +		return NULL;
> > +	}
> > +	list_del(&buf->list);
> > +	spin_unlock(&vdev->slock);
> 
> Can this be simplified by going:
> spin_lock();
> buf = list_first_entry_or_null(...);
> if (buf) list_del(...);
> spin_unlock();
> return buf;
> 

Ok, we will simplify this implementation as you suggested.

> > +
> > +	return buf;
> > +}
> > +
> > +int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev)
> 
> This only ever returns 0, so make it a void function.
> 

Ok, fix in next patch.

> > +{
> > +	unsigned int node;
> > +	const int mtk_cam_dev_node_num = cam_dev->dev_node_num;
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	struct mtk_cam_dev_start_param s_param;
> > +	struct mtk_cam_dev_buffer *buf;
> > +
> > +	memset(&s_param, 0, sizeof(struct mtk_cam_dev_start_param));
> > +
> > +	if (!cam_dev->streaming) {
> > +		dev_dbg(dev, "%s: stream off, no enqueue\n", __func__);
> > +		return 0;
> > +	}
> > +
> > +	/* Check all enabled nodes to collect its buffer  */
> > +	for (node = 0; node < mtk_cam_dev_node_num; node++) {
> > +		if (!cam_dev->mem2mem2_nodes[node].enabled)
> > +			continue;
> > +		buf = mtk_cam_dev_get_pending_buf(cam_dev, node);
> > +		if (!buf)
> > +			continue;
> > +
> > +		/* TBD: use buf_init callback function */
> > +		buf->daddr =
> > +			vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
> > +		if (cam_dev->mem2mem2_nodes[node].desc.smem_alloc) {
> > +			buf->scp_addr = mtk_cam_smem_iova_to_scp_addr(
> > +				cam_dev->smem_dev, buf->daddr);
> > +		} else {
> > +			buf->scp_addr = 0;
> > +		}
> > +
> > +		dev_dbg(dev,
> > +			"Node:%d fd:%d idx:%d state:%d daddr:%pad addr:%pad",
> > +			node,
> > +			buf->vbb.request_fd,
> > +			buf->vbb.vb2_buf.index,
> > +			buf->vbb.vb2_buf.state,
> > +			&buf->daddr,
> > +			&buf->scp_addr);
> > +
> > +		s_param.buffers[node] = buf;
> > +		s_param.request_fd = buf->vbb.request_fd;
> > +	}
> > +
> > +	/* Trigger en-queued job to driver */
> > +	mtk_isp_req_enqueue(dev, &s_param);
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_dev_init(struct platform_device *pdev,
> > +		     struct mtk_cam_dev *cam_dev)
> > +{
> > +	int ret;
> > +
> > +	cam_dev->pdev = pdev;
> > +
> > +	mtk_cam_dev_queue_setup(cam_dev, &queues_setting);
> > +
> > +	/* v4l2 sub-device registration */
> > +	dev_dbg(&cam_dev->pdev->dev, "mem2mem2.name: %s\n",
> > +		MTK_CAM_DEV_P1_NAME);
> > +
> > +	ret = mtk_cam_mem2mem2_v4l2_register(cam_dev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = mtk_cam_v4l2_async_register(cam_dev);
> > +	if (ret)
> 
> If this fails do we need to undo the stuff done in
> mtk_cam_mem2mem2_v4l2_register?
> 

Yes, we will call mtk_cam_v4l2_unregister(cam_dev) if failed in
mtk_cam_v4l2_async_register function in next patch.


> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_dev_release(struct platform_device *pdev,
> > +			struct mtk_cam_dev *cam_dev)
> > +{
> > +	mtk_cam_v4l2_async_unregister(cam_dev);
> > +	mtk_cam_v4l2_unregister(cam_dev);
> > +
> > +	return 0;
> > +}
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
> > new file mode 100644
> > index 000000000000..410460de44fa
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-dev.h
> > @@ -0,0 +1,250 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018 Mediatek Corporation.
> > + * Copyright (c) 2017 Intel Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License version
> > + * 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * MTK_CAM-dev is highly based on Intel IPU3 ImgU driver.
> > + *
> > + */
> > +
> > +#ifndef __MTK_CAM_DEV_H__
> > +#define __MTK_CAM_DEV_H__
> > +
> > +#include <linux/device.h>
> > +#include <linux/types.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/videodev2.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-subdev.h>
> > +#include <media/videobuf2-core.h>
> > +#include <media/videobuf2-v4l2.h>
> > +
> > +#define MTK_CAM_DEV_P1_NAME		"MTK-ISP-P1-V4L2"
> > +
> > +#define MTK_CAM_DEV_NODES		11
> > +
> > +#define MTK_CAM_P1_META_IN_0		0
> > +#define MTK_CAM_P1_TOTAL_OUTPUT		1
> > +
> > +#define MTK_CAM_P1_MAIN_STREAM_OUT	1
> > +#define MTK_CAM_P1_PACKED_BIN_OUT	2
> > +#define MTK_CAM_P1_META_OUT_0		3
> > +#define MTK_CAM_P1_META_OUT_1		4
> > +#define MTK_CAM_P1_META_OUT_2		5
> > +#define MTK_CAM_P1_META_OUT_3		6
> > +#define MTK_CAM_P1_TOTAL_CAPTURE	6
> 
> Please align macro values using tabs.
> 

Ok, we will fix this coding style issue in next patch.

> > +
> > +struct mtk_cam_dev_buffer {
> > +	struct vb2_v4l2_buffer	vbb;
> > +	struct list_head	list;
> > +	/* Intenal part */
> > +	dma_addr_t		daddr;
> > +	dma_addr_t		scp_addr;
> > +};
> > +
> > +/* Attributes setup by device owner */
> > +struct mtk_cam_dev_queues_setting {
> > +	const struct mtk_cam_dev_node_desc *output_node_descs;
> > +	unsigned int total_output_nodes;
> > +	const struct mtk_cam_dev_node_desc *capture_node_descs;
> > +	unsigned int total_capture_nodes;
> > +};
> > +
> > +struct mtk_cam_dev_start_param {
> > +	int request_fd;
> > +	struct mtk_cam_dev_buffer *buffers[MTK_CAM_DEV_NODES];
> > +};
> > +
> > +struct mtk_cam_dev_finish_param {
> > +	int request_fd;
> > +	unsigned int frame_seq_no;
> > +	unsigned int state;
> > +	struct list_head *list_buf;
> > +};
> > +
> > +/*
> > + * struct mtk_cam_dev_node_desc - node attributes
> > + *
> > + * @id:		 id of the context queue
> > + * @name:	 media entity name
> > + * @description: descritpion of node
> > + * @cap:	 mapped to V4L2 capabilities
> > + * @buf_type:	 mapped to V4L2 buffer type
> > + * @dma_port:	 the dma port associated to the buffer
> > + * @link_flags:	 default media link flags
> > + * @smem_alloc:	 using the cam_smem_drv as alloc ctx or not
> > + * @capture:	 true for capture queue (device to user)
> > + *		 false for output queue (from user to device)
> > + * @image:	 true for image node, false for meta node
> > + * @num_fmts:	 the number of supported formats
> > + * @default_fmt_idx: default format of this node
> > + * @max_buf_count: maximum V4L2 buffer count
> > + * @ioctl_ops:  mapped to v4l2_ioctl_ops
> > + * @fmts:	supported format
> > + *
> > + */
> > +struct mtk_cam_dev_node_desc {
> > +	u8 id;
> > +	char *name;
> > +	char *description;
> > +	u32 cap;
> > +	u32 buf_type;
> > +	u32 dma_port;
> > +	u32 link_flags;
> > +	u8 smem_alloc:1;
> > +	u8 capture:1;
> > +	u8 image:1;
> > +	u8 num_fmts;
> > +	u8 default_fmt_idx;
> > +	u8 max_buf_count;
> > +	const struct v4l2_ioctl_ops *ioctl_ops;
> > +	struct v4l2_format *fmts;
> > +};
> > +
> > +/*
> > + * struct mtk_cam_video_device - Mediatek video device structure.
> > + *
> > + * @id:		Id for mtk_cam_dev_node_desc or mem2mem2_nodes array
> > + * @enabled:	Indicate the device is enabled or not
> > + * @vdev_fmt:	The V4L2 format of video device
> > + * @vdev_apd:	The media pad graph object of video device
> > + * @vbq:	A videobuf queue of video device
> > + * @desc:	The node attributes of video device
> > + * @ctrl_handler:	The control handler of video device
> > + * @pending_list:	List for pending buffers before enqueuing into driver
> > + * @lock:	Serializes vb2 queue and video device operations.
> > + * @slock:	Protect for pending_list.
> > + *
> > + */
> > +struct mtk_cam_video_device {
> > +	unsigned int id;
> > +	unsigned int enabled;
> > +	struct v4l2_format vdev_fmt;
> > +	struct video_device vdev;
> > +	struct media_pad vdev_pad;
> > +	struct vb2_queue vbq;
> > +	struct mtk_cam_dev_node_desc desc;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct list_head pending_list;
> > +	/* Used for vbq & vdev */
> > +	struct mutex lock;
> > +	/* protect for pending_list */
> > +	spinlock_t slock;
> > +};
> > +
> > +/*
> > + * struct mtk_cam_dev - Mediatek camera device structure.
> > + *
> > + * @pdev:	Pointer to platform device
> > + * @smem_pdev:	Pointer to shared memory platform device
> > + * @pipeline:	Media pipeline information
> > + * @media_dev:	Media device
> > + * @subdev:	The V4L2 sub-device
> > + * @v4l2_dev:	The V4L2 device driver
> > + * @notifier:	The v4l2_device notifier data
> > + * @subdev_pads: Pointer to the number of media pads of this sub-device
> > + * @ctrl_handler: The control handler
> > + * @mem2mem2_nodes: The array list of mtk_cam_video_device
> > + * @seninf:	Pointer to the seninf sub-device
> > + * @sensor:	Pointer to the active sensor V4L2 sub-device when streaming on
> > + * @streaming:	Indicate the overall streaming status is on or off
> > + * @dev_node_num: The number of supported V4L2 video device nodes
> > + * @request_fd:	The file descriptor of request API
> > + * @request_count: The buffer count of request API
> > + *
> > + * Below is the graph topology for Camera IO connection.
> > + * sensor 1 (main) --> sensor IF --> P1 sub-device
> > + * sensor 2 (sub)  -->
> > + *
> > + */
> > +struct mtk_cam_dev {
> > +	struct platform_device *pdev;
> > +	struct device *smem_dev;
> > +	struct media_pipeline pipeline;
> > +	struct media_device media_dev;
> > +	struct v4l2_subdev subdev;
> > +	struct v4l2_device v4l2_dev;
> > +	struct v4l2_async_notifier notifier;
> > +	struct media_pad *subdev_pads;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct mtk_cam_video_device mem2mem2_nodes[MTK_CAM_DEV_NODES];
> > +	struct v4l2_subdev *seninf;
> > +	struct v4l2_subdev *sensor;
> > +	unsigned int streaming;
> > +	unsigned int dev_node_num;
> > +	int request_fd;
> > +	unsigned int request_count;
> > +};
> > +
> > +int mtk_cam_dev_init(struct platform_device *pdev,
> > +		     struct mtk_cam_dev *cam_dev);
> > +int mtk_cam_v4l2_register(struct device *dev,
> > +			  struct media_device *media_dev,
> > +			  struct v4l2_device *v4l2_dev,
> > +			  struct v4l2_ctrl_handler *ctrl_handler);
> > +int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev);
> > +int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev);
> > +int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev);
> > +void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev);
> > +int mtk_cam_dev_queue_req_buffers(struct mtk_cam_dev *cam_dev);
> > +int mtk_cam_dev_release(struct platform_device *pdev,
> > +			struct mtk_cam_dev *cam_dev);
> > +void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev,
> > +			     const struct mtk_cam_dev_queues_setting *setting);
> > +int mtk_cam_dev_job_finish(struct mtk_cam_dev *cam_dev,
> > +			   struct mtk_cam_dev_finish_param *param);
> > +int mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
> > +				 __u32 frame_seq_no);
> > +void mtk_cam_dev_fmt_set_img(struct device *dev,
> > +			     struct v4l2_pix_format_mplane *dest_fmt,
> > +			     struct v4l2_pix_format_mplane *src_fmt,
> > +			     unsigned int node_id);
> > +struct v4l2_format *
> > +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *queue_desc, u32 format);
> > +void mtk_cam_dev_load_default_fmt(struct device *dev,
> > +				  struct mtk_cam_dev_node_desc *queue,
> > +				  struct v4l2_format *dest_fmt);
> > +void mtk_cam_dev_cal_mplane_pix_fmt(struct device *dev,
> > +				    struct v4l2_pix_format_mplane *dest_fmt,
> > +				    unsigned int node_id);
> > +
> > +static inline struct mtk_cam_video_device *
> > +file_to_mtk_cam_node(struct file *__file)
> > +{
> > +	return container_of(video_devdata(__file),
> > +		struct mtk_cam_video_device, vdev);
> > +}
> > +
> > +static inline struct mtk_cam_dev *
> > +mtk_cam_subdev_to_dev(struct v4l2_subdev *__sd)
> > +{
> > +	return container_of(__sd,
> > +		struct mtk_cam_dev, subdev);
> > +}
> > +
> > +static inline struct mtk_cam_video_device *
> > +mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
> > +{
> > +	return container_of(__vq,
> > +		struct mtk_cam_video_device, vbq);
> > +}
> > +
> > +static inline struct mtk_cam_dev_buffer *
> > +mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
> > +{
> > +	return container_of(__vb,
> > +		struct mtk_cam_dev_buffer, vbb.vb2_buf);
> > +}
> > +
> > +#endif /* __MTK_CAM_DEV_H__ */
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
> > new file mode 100644
> > index 000000000000..196aaef3d854
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
> > @@ -0,0 +1,1086 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018 Mediatek Corporation.
> > + * Copyright (c) 2017 Intel Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License version
> > + * 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * MTK_CAM-v4l2 is highly based on Intel IPU3 ImgU driver.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/videodev2.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +#include <media/v4l2-subdev.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <linux/device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <linux/of_graph.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-async.h>
> > +
> > +#include "mtk_cam.h"
> > +#include "mtk_cam-ctrl.h"
> > +#include "mtk_cam-dev.h"
> > +#include "mtk_cam-v4l2-util.h"
> > +
> > +#define MTK_CAM_SENINF_PAD_SRC			4
> > +#define MTK_CAM_P1_HUB_PAD_SINK			MTK_CAM_DEV_NODES
> > +
> > +static int mtk_cam_subdev_open(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_fh *fh)
> > +{
> > +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> > +
> > +	cam_dev->request_fd = -1;
> > +	cam_dev->request_count = 0;
> > +
> > +	return mtk_isp_open(&cam_dev->pdev->dev);
> > +}
> > +
> > +static int mtk_cam_subdev_close(struct v4l2_subdev *sd,
> > +				struct v4l2_subdev_fh *fh)
> > +{
> > +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> > +
> > +	return mtk_isp_release(&cam_dev->pdev->dev);
> > +}
> > +
> > +static int mtk_cam_v4l2_get_active_sensor(struct mtk_cam_dev *cam_dev)
> 
> "get" implies that this function will retrieve something without
> side effects, which is not the case here. In the error case, the return
> value is ignored by the caller as well.
> 
> Consider making this function return a struct v4l2_subdev* (or NULL in
> the error case) and let the caller set mtk_cam_dev::sensor.
> 

Ok, below is the new function prototype.
static struct v4l2_subdev *
mtk_cam_cio_get_active_sensor(struct mtk_cam_dev *cam_dev)

> > +{
> > +	struct media_device *mdev = cam_dev->seninf->entity.graph_obj.mdev;
> > +	struct media_entity *entity;
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +
> > +	cam_dev->sensor = NULL;
> > +	media_device_for_each_entity(entity, mdev) {
> > +		dev_dbg(dev, "media entity: %s:0x%x\n",
> > +			entity->name, entity->function);
> > +		if (entity->function == MEDIA_ENT_F_CAM_SENSOR &&
> > +		    entity->stream_count > 0) {
> > +			cam_dev->sensor = media_entity_to_v4l2_subdev(entity);
> > +			dev_dbg(dev, "Sensor found: %s\n", entity->name);
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (!cam_dev->sensor) {
> > +		dev_err(dev, "Sensor is not connected\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam_dev)
> > +{
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	int ret;
> > +
> > +	/* Align vb2_core_streamon design */
> > +	if (cam_dev->streaming) {
> > +		dev_warn(dev, "already streaming\n", dev);
> > +		return 0;
> > +	}
> > +
> > +	if (!cam_dev->seninf) {
> > +		dev_err(dev, "no seninf connected:%d\n", ret);
> > +		return -EPERM;
> > +	}
> > +
> > +	/* Get active sensor from graph topology */
> > +	ret = mtk_cam_v4l2_get_active_sensor(cam_dev);
> > +	if (ret)
> > +		return -EPERM;
> > +
> > +	ret = mtk_isp_config(dev);
> > +	if (ret)
> > +		return -EPERM;
> > +
> > +	/* Seninf must stream on first */
> > +	dev_dbg(dev, "streamed on: %s\n", cam_dev->seninf->entity.name);
> > +	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);
> > +	if (ret) {
> > +		dev_err(dev, "%s stream on failed:%d\n",
> > +			cam_dev->seninf->entity.name, ret);
> > +		return -EPERM;
> > +	}
> > +
> > +	dev_dbg(dev, "streamed on: %s\n", cam_dev->sensor->entity.name);
> > +	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 1);
> > +	if (ret) {
> > +		dev_err(dev, "%s stream on failed:%d\n",
> > +			cam_dev->sensor->entity.name, ret);
> > +		goto fail_sensor_on;
> > +	}
> > +
> > +	cam_dev->streaming = true;
> > +	mtk_cam_dev_queue_req_buffers(cam_dev);
> > +	isp_composer_stream(isp_ctx, 1);
> > +	dev_dbg(dev, "streamed on Pass 1\n");
> > +
> > +	return 0;
> > +
> > +fail_sensor_on:
> > +	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
> > +	return -EPERM;
> > +}
> > +
> > +static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam_dev)
> > +{
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	struct isp_p1_device *p1_dev = get_p1_device(dev);
> > +	struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx;
> > +	int ret;
> > +
> > +	if (!cam_dev->streaming) {
> > +		dev_warn(dev, "already stream off");
> > +		return 0;
> > +	}
> > +
> > +	dev_dbg(dev, "stream off: %s\n", cam_dev->sensor->entity.name);
> > +	ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0);
> > +	if (ret) {
> > +		dev_err(dev, "%s stream off failed:%d\n",
> > +			cam_dev->sensor->entity.name, ret);
> > +		return -EPERM;
> > +	}
> > +
> > +	dev_dbg(dev, "stream off: %s\n", cam_dev->seninf->entity.name);
> > +	ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0);
> > +	if (ret) {
> > +		dev_err(dev, "%s stream off failed:%d\n",
> > +			cam_dev->seninf->entity.name, ret);
> > +		goto fail_sensor_off;
> > +	}
> > +
> > +	isp_composer_stream(isp_ctx, 0);
> > +	cam_dev->streaming = false;
> > +	dev_dbg(dev, "streamed off Pass 1\n");
> > +
> > +	return 0;
> > +
> > +fail_sensor_off:
> > +	v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1);
> 
> I'd be interested to get Tomasz's input here. If we fail to stream off
> one of the subdevs, should we stream on the other one? What if that
> fails? It's not clear to me the expectation when stream off fails, but
> this seems a bit odd.
> 

Ok, maybe we just return the error code to the user space in this case.
No need to perform stream on the other one.
If you have any better suggestion, please let us know.

> > +	return -EPERM;
> > +}
> > +
> > +static int mtk_cam_subdev_s_stream(struct v4l2_subdev *sd,
> > +				   int enable)
> 
> This can fit on one line.
> 

Fix in next patch.

> > +{
> > +	struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd);
> > +
> > +	if (enable)
> > +		return mtk_cam_cio_stream_on(cam_dev);
> > +	else
> > +		return mtk_cam_cio_stream_off(cam_dev);
> > +}
> > +
> > +static int mtk_cam_subdev_subscribe_event(struct v4l2_subdev *subdev,
> > +					  struct v4l2_fh *fh,
> > +					  struct v4l2_event_subscription *sub)
> > +{
> > +	switch (sub->type) {
> > +	case V4L2_EVENT_FRAME_SYNC:
> > +		return v4l2_event_subscribe(fh, sub, 0, NULL);
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int mtk_cam_link_setup(struct media_entity *entity,
> > +			      const struct media_pad *local,
> > +	const struct media_pad *remote, u32 flags)
> 
> Strange indentation here.
> 

Fix in next patch.

> > +{
> > +	struct mtk_cam_dev *cam_dev =
> > +		container_of(entity, struct mtk_cam_dev, subdev.entity);
> > +	u32 pad = local->index;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "link setup: %d -> %d\n",
> > +		pad, remote->index);
> > +
> > +	if (pad < cam_dev->dev_node_num)
> > +		cam_dev->mem2mem2_nodes[pad].enabled =
> > +			!!(flags & MEDIA_LNK_FL_ENABLED);
> > +
> > +	return 0;
> > +}
> > +
> > +static void mtk_cam_dev_queue_buffers(struct vb2_buffer *vb)
> > +{
> > +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vb->vb2_queue);
> > +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> > +	struct mtk_cam_dev_buffer *buf;
> > +
> > +	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> > +	buf->daddr = vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0);
> > +	buf->scp_addr = 0;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "%pad:%pad\n",
> > +		&buf->daddr, &buf->scp_addr);
> > +
> > +	mtk_isp_enqueue(&cam_dev->pdev->dev, node->desc.dma_port, buf);
> > +}
> > +
> > +static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
> > +{
> > +	struct mtk_cam_dev *mtk_cam_dev = vb2_get_drv_priv(vb->vb2_queue);
> > +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> > +	struct device *dev = &mtk_cam_dev->pdev->dev;
> > +	struct mtk_cam_dev_buffer *buf;
> > +	struct vb2_v4l2_buffer *v4l2_buf;
> > +
> > +	buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> > +	v4l2_buf = to_vb2_v4l2_buffer(vb);
> > +
> > +	dev_dbg(dev, "%s: node:%d fd:%d idx:%d\n",
> > +		__func__,
> > +		node->id,
> > +		v4l2_buf->request_fd,
> > +		v4l2_buf->vb2_buf.index);
> > +
> > +	if (v4l2_buf->request_fd < 0) {
> > +		mtk_cam_dev_queue_buffers(vb);
> > +		return;
> > +	}
> > +
> > +	if (mtk_cam_dev->request_fd != v4l2_buf->request_fd) {
> > +		mtk_cam_dev->request_fd = v4l2_buf->request_fd;
> > +		mtk_cam_dev->request_count =
> > +			vb->req_obj.req->num_incomplete_objects;
> > +		dev_dbg(dev, "init  mtk_cam_dev_buf, fd(%d) count(%d)\n",
> > +			v4l2_buf->request_fd,
> > +			vb->req_obj.req->num_incomplete_objects);
> > +	}
> > +
> > +	/* Added the buffer into the tracking list */
> > +	spin_lock(&node->slock);
> > +	list_add_tail(&buf->list, &node->pending_list);
> > +	spin_unlock(&node->slock);
> > +
> > +	mtk_cam_dev->request_count--;
> > +
> > +	if (!mtk_cam_dev->request_count) {
> > +		mtk_cam_dev->request_fd = -1;
> > +		mtk_cam_dev_queue_req_buffers(mtk_cam_dev);
> > +	}
> > +}
> > +
> > +static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
> > +				   unsigned int *num_buffers,
> > +				   unsigned int *num_planes,
> > +				   unsigned int sizes[],
> > +				   struct device *alloc_devs[])
> > +{
> > +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> > +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	unsigned int max_buffer_count = node->desc.max_buf_count;
> > +	const struct v4l2_format *fmt = &node->vdev_fmt;
> > +	unsigned int size;
> > +
> > +	/* Check the limitation of buffer size */
> > +	if (max_buffer_count > 0)
> > +		*num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
> > +	else
> > +		*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
> > +
> > +	if (node->desc.smem_alloc) {
> > +		alloc_devs[0] = cam_dev->smem_dev;
> > +		dev_dbg(dev, "Select smem alloc_devs(0x%pK)\n", alloc_devs[0]);
> > +	} else {
> > +		alloc_devs[0] = &cam_dev->pdev->dev;
> > +		dev_dbg(dev, "Select default alloc_devs(0x%pK)\n",
> > +			alloc_devs[0]);
> > +	}
> > +
> > +	if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
> > +	    vq->type == V4L2_BUF_TYPE_META_CAPTURE)
> > +		size = fmt->fmt.meta.buffersize;
> > +	else
> > +		size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
> > +
> > +	/* Validate initialized num_planes & size[0] */
> > +	if (*num_planes) {
> > +		if (sizes[0] < size)
> > +			return -EINVAL;
> > +	} else {
> > +		*num_planes = 1;
> > +		sizes[0] = size;
> > +	}
> > +
> > +	/* Initialize buffer queue & locks */
> > +	INIT_LIST_HEAD(&node->pending_list);
> > +	mutex_init(&node->lock);
> > +	spin_lock_init(&node->slock);
> 
> Aren't these initialized in mtk_cam_mem2mem2_v4l2_register?
> 

Yes, we will remove these initialization and only keep them in
mtk_cam_mem2mem2_v4l2_register function.

> > +
> > +	return 0;
> > +}
> > +
> > +static bool
> > +mtk_cam_all_nodes_streaming(struct mtk_cam_dev *cam_dev,
> > +			    struct mtk_cam_video_device *except)
> > +{
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> > +		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
> > +
> > +		if (node == except)
> > +			continue;
> > +		if (node->enabled && !vb2_start_streaming_called(&node->vbq))
> > +			return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static void mtk_cam_return_all_buffers(struct mtk_cam_dev *cam_dev,
> > +				       struct mtk_cam_video_device *node,
> > +				       enum vb2_buffer_state state)
> > +{
> > +	struct mtk_cam_dev_buffer *b, *b0;
> > +	unsigned int i;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "%s: node:%s", __func__, node->vdev.name);
> > +
> > +	/* Return all buffers */
> > +	spin_lock(&node->slock);
> > +	list_for_each_entry_safe(b, b0, &node->pending_list, list) {
> > +		list_del(&b->list);
> > +	}
> > +	spin_unlock(&node->slock);
> > +
> > +	for (i = 0; i < node->vbq.num_buffers; ++i)
> > +		if (node->vbq.bufs[i]->state == VB2_BUF_STATE_ACTIVE)
> > +			vb2_buffer_done(node->vbq.bufs[i], state);
> > +}
> > +
> > +static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
> > +				       unsigned int count)
> > +{
> > +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> > +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> > +	int ret;
> > +
> > +	if (!node->enabled) {
> > +		dev_err(&cam_dev->pdev->dev, "Node:%d is not enable\n",
> > +			node->id);
> > +		ret = -ENOLINK;
> > +		goto fail_return_bufs;
> > +	}
> > +
> > +	ret = media_pipeline_start(&node->vdev.entity, &cam_dev->pipeline);
> > +	if (ret < 0) {
> > +		dev_err(&cam_dev->pdev->dev, "Node:%d %s failed\n",
> > +			node->id, __func__);
> > +		goto fail_return_bufs;
> > +	}
> > +
> > +	if (!mtk_cam_all_nodes_streaming(cam_dev, node))
> > +		return 0;
> > +
> > +	/* Start streaming of the whole pipeline now */
> > +	ret = v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 1);
> > +	if (ret < 0) {
> > +		dev_err(&cam_dev->pdev->dev, "Node:%d s_stream failed\n",
> > +			node->id);
> > +		goto fail_stop_pipeline;
> > +	}
> > +	return 0;
> > +
> > +fail_stop_pipeline:
> > +	media_pipeline_stop(&node->vdev.entity);
> > +fail_return_bufs:
> > +	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_QUEUED);
> > +	return ret;
> > +}
> > +
> > +static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
> > +{
> > +	struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq);
> > +	struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> > +
> > +	/* Was this the first node with streaming disabled? */
> > +	if (mtk_cam_all_nodes_streaming(cam_dev, node)) {
> > +		/* Yes, really stop streaming now */
> > +		if (v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 0))
> > +			dev_err(&cam_dev->pdev->dev,
> > +				"failed to stop streaming\n");
> > +	}
> > +	mtk_cam_return_all_buffers(cam_dev, node, VB2_BUF_STATE_ERROR);
> > +	media_pipeline_stop(&node->vdev.entity);
> > +}
> > +
> > +int mtk_cam_videoc_querycap(struct file *file, void *fh,
> > +			    struct v4l2_capability *cap)
> > +{
> > +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> > +
> > +	strscpy(cap->driver, MTK_CAM_DEV_P1_NAME, sizeof(cap->driver));
> > +	strscpy(cap->card, MTK_CAM_DEV_P1_NAME, sizeof(cap->card));
> > +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> > +		 dev_name(cam_dev->media_dev.dev));
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
> > +			    struct v4l2_fmtdesc *f)
> > +{
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +
> > +	if (f->index >= node->desc.num_fmts || f->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat;
> > +	f->flags = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +
> > +	if (f->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	f->fmt = node->vdev_fmt.fmt;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_videoc_try_fmt(struct file *file, void *fh,
> > +			   struct v4l2_format *in_fmt)
> > +{
> > +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +	struct v4l2_format *dev_fmt;
> > +	__u32  width, height;
> > +
> > +	if (in_fmt->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "%s: fmt:%c%c%c%c, w*h:%u*%u\n",
> > +		__func__,
> > +		(in_fmt->fmt.pix_mp.pixelformat & 0xFF),
> > +		(in_fmt->fmt.pix_mp.pixelformat >> 8) & 0xFF,
> > +		(in_fmt->fmt.pix_mp.pixelformat >> 16) & 0xFF,
> > +		(in_fmt->fmt.pix_mp.pixelformat >> 24) & 0xFF,
> > +		in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height);
> > +
> > +	width = in_fmt->fmt.pix_mp.width;
> > +	height = in_fmt->fmt.pix_mp.height;
> > +
> > +	dev_fmt = mtk_cam_dev_find_fmt(&node->desc,
> > +				       in_fmt->fmt.pix_mp.pixelformat);
> > +	if (dev_fmt) {
> > +		mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
> > +					&in_fmt->fmt.pix_mp,
> > +					&dev_fmt->fmt.pix_mp,
> > +					node->id);
> > +	} else {
> > +		mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
> > +					     &node->desc,
> > +					     in_fmt);
> > +	}
> > +	in_fmt->fmt.pix_mp.width = clamp_t(u32,
> > +					   width,
> > +					   CAM_MIN_WIDTH,
> > +					   in_fmt->fmt.pix_mp.width);
> > +	in_fmt->fmt.pix_mp.height = clamp_t(u32,
> > +					    height,
> > +					    CAM_MIN_HEIGHT,
> > +					    in_fmt->fmt.pix_mp.height);
> > +	mtk_cam_dev_cal_mplane_pix_fmt(&cam_dev->pdev->dev,
> > +				       &in_fmt->fmt.pix_mp,
> > +				       node->id);
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > +	struct mtk_cam_dev *cam_dev = video_drvdata(file);
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +
> > +	if (f->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	if (cam_dev->streaming)
> > +		return -EBUSY;
> > +
> > +	/* Get the valid format */
> > +	mtk_cam_videoc_try_fmt(file, fh, f);
> > +
> > +	/* Configure to video device */
> > +	mtk_cam_dev_fmt_set_img(&cam_dev->pdev->dev,
> > +				&node->vdev_fmt.fmt.pix_mp,
> > +				&f->fmt.pix_mp,
> > +				node->id);
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
> > +			      struct v4l2_input *input)
> > +{
> > +	if (input->index > 0)
> > +		return -EINVAL;
> > +
> > +	strscpy(input->name, "camera", sizeof(input->name));
> > +	input->type = V4L2_INPUT_TYPE_CAMERA;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
> > +{
> > +	*input = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input)
> > +{
> > +	return input == 0 ? 0 : -EINVAL;
> > +}
> > +
> > +int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
> > +				   const struct v4l2_event_subscription *sub)
> > +{
> > +	switch (sub->type) {
> > +	case V4L2_EVENT_CTRL:
> > +		return v4l2_ctrl_subscribe_event(fh, sub);
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +int mtk_cam_enum_framesizes(struct file *filp, void *priv,
> > +			    struct v4l2_frmsizeenum *sizes)
> > +{
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
> > +	struct v4l2_format *dev_fmt;
> > +
> > +	dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
> > +	if (!dev_fmt || sizes->index)
> > +		return -EINVAL;
> > +
> > +	if (node->id == MTK_CAM_P1_MAIN_STREAM_OUT) {
> > +		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> > +		sizes->stepwise.max_width = IMG_MAX_WIDTH;
> > +		sizes->stepwise.min_width = IMG_MIN_WIDTH;
> > +		sizes->stepwise.max_height = IMG_MAX_HEIGHT;
> > +		sizes->stepwise.min_height = IMG_MIN_HEIGHT;
> > +		sizes->stepwise.step_height = 1;
> > +		sizes->stepwise.step_width = 1;
> > +	} else if (node->id == MTK_CAM_P1_PACKED_BIN_OUT) {
> > +		sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> > +		sizes->stepwise.max_width = RRZ_MAX_WIDTH;
> > +		sizes->stepwise.min_width = RRZ_MIN_WIDTH;
> > +		sizes->stepwise.max_height = RRZ_MAX_HEIGHT;
> > +		sizes->stepwise.min_height = RRZ_MIN_HEIGHT;
> > +		sizes->stepwise.step_height = 1;
> > +		sizes->stepwise.step_width = 1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_meta_enum_format(struct file *file, void *fh,
> > +			     struct v4l2_fmtdesc *f)
> > +{
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +
> > +	/* Each node is dedicated to only one meta format */
> > +	if (f->index > 0 || f->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	strscpy(f->description, node->desc.description,
> > +		sizeof(node->desc.description));
> > +	f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
> > +
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
> > +			      struct v4l2_format *f)
> > +{
> > +	struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> > +
> > +	/* Each node is dedicated to only one meta format */
> > +	if (f->type != node->vbq.type)
> > +		return -EINVAL;
> > +
> > +	f->fmt = node->vdev_fmt.fmt;
> > +
> > +	return 0;
> > +}
> > +
> > +/* subdev internal operations */
> > +static const struct v4l2_subdev_internal_ops mtk_cam_subdev_internal_ops = {
> > +	.open = mtk_cam_subdev_open,
> > +	.close = mtk_cam_subdev_close,
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = {
> > +	.subscribe_event = mtk_cam_subdev_subscribe_event,
> > +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> > +};
> > +
> > +static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = {
> > +	.s_stream = mtk_cam_subdev_s_stream,
> > +};
> > +
> > +static const struct v4l2_subdev_ops mtk_cam_subdev_ops = {
> > +	.core = &mtk_cam_subdev_core_ops,
> > +	.video = &mtk_cam_subdev_video_ops,
> > +};
> > +
> > +static const struct media_entity_operations mtk_cam_media_ops = {
> > +	.link_setup = mtk_cam_link_setup,
> > +	.link_validate = v4l2_subdev_link_validate,
> > +};
> > +
> > +static void mtk_cam_vb2_buf_request_complete(struct vb2_buffer *vb)
> > +{
> > +	struct mtk_cam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> > +
> > +	v4l2_ctrl_request_complete(vb->req_obj.req,
> > +				   dev->v4l2_dev.ctrl_handler);
> > +}
> 
> Move this function up with the other mtk_cam_vb2_* functions.
> 

Fix in next patch.

> > +
> > +static const struct vb2_ops mtk_cam_vb2_ops = {
> > +	.buf_queue = mtk_cam_vb2_buf_queue,
> > +	.queue_setup = mtk_cam_vb2_queue_setup,
> > +	.start_streaming = mtk_cam_vb2_start_streaming,
> > +	.stop_streaming = mtk_cam_vb2_stop_streaming,
> > +	.wait_prepare = vb2_ops_wait_prepare,
> > +	.wait_finish = vb2_ops_wait_finish,
> > +	.buf_request_complete = mtk_cam_vb2_buf_request_complete,
> > +};
> > +
> > +static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
> > +	.unlocked_ioctl = video_ioctl2,
> > +	.open = v4l2_fh_open,
> > +	.release = vb2_fop_release,
> > +	.poll = vb2_fop_poll,
> > +	.mmap = vb2_fop_mmap,
> > +#ifdef CONFIG_COMPAT
> > +	.compat_ioctl32 = v4l2_compat_ioctl32,
> > +#endif
> > +};
> > +
> > +/*
> > + * Config node's video properties
> > + * according to the device context requirement
> > + */
> > +static void mtk_cam_node_to_v4l2(struct mtk_cam_dev *cam_dev,
> > +				 unsigned int node,
> > +				 struct video_device *vdev,
> > +				 struct v4l2_format *f)
> > +{
> > +	struct mtk_cam_dev_node_desc *node_desc =
> > +		&cam_dev->mem2mem2_nodes[node].desc;
> > +
> > +	/* set cap/type/ioctl_ops of the video device */
> > +	vdev->device_caps = V4L2_CAP_STREAMING | node_desc->cap;
> > +	f->type = node_desc->buf_type;
> > +	vdev->ioctl_ops = node_desc->ioctl_ops;
> > +
> > +	mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev,
> > +				     node_desc,
> > +				     f);
> > +}
> > +
> > +static const struct media_device_ops mtk_cam_media_req_ops = {
> > +	.req_validate = vb2_request_validate,
> > +	.req_queue = vb2_request_queue,
> > +};
> > +
> > +static int mtk_cam_media_register(struct device *dev,
> > +				  struct media_device *media_dev)
> > +{
> > +	int ret;
> > +
> > +	media_dev->dev = dev;
> > +	strscpy(media_dev->model, MTK_CAM_DEV_P1_NAME,
> > +		sizeof(media_dev->model));
> > +	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
> > +		 "platform:%s", dev_name(dev));
> > +	media_dev->hw_revision = 0;
> > +	media_device_init(media_dev);
> > +	media_dev->ops = &mtk_cam_media_req_ops;
> > +	dev_info(dev, "Register media device: %s, 0x%pK",
> > +		 MTK_CAM_DEV_P1_NAME, media_dev);
> > +
> > +	ret = media_device_register(media_dev);
> > +	if (ret) {
> > +		dev_err(dev, "failed to register media device (%d)\n", ret);
> > +		goto fail_v4l2_dev;
> > +	}
> > +
> > +	return 0;
> > +
> > +fail_v4l2_dev:
> > +	media_device_unregister(media_dev);
> > +	media_device_cleanup(media_dev);
> > +
> > +	return ret;
> > +}
> > +
> > +int mtk_cam_v4l2_register(struct device *dev,
> > +			  struct media_device *media_dev,
> > +			  struct v4l2_device *v4l2_dev,
> > +			  struct v4l2_ctrl_handler *ctrl_handler)
> 
> This can be a static function.
> 

After reveiw, we will remove this function and move the source code to
the mtk_cam_mem2mem2_v4l2_register. Below error handling will be
removed, either.

> > +{
> > +	int ret;
> > +
> > +	/* Set up v4l2 device */
> > +	v4l2_dev->ctrl_handler = ctrl_handler;
> > +	v4l2_dev->mdev = media_dev;
> > +	dev_info(dev, "Register v4l2 device: 0x%pK", v4l2_dev);
> > +	ret = v4l2_device_register(dev, v4l2_dev);
> > +	if (ret) {
> > +		dev_err(dev, "failed to register V4L2 device (%d)\n", ret);
> > +		goto fail_v4l2_dev;
> > +	}
> > +
> > +	return 0;
> > +
> > +fail_v4l2_dev:
> > +	media_device_unregister(media_dev);
> > +	media_device_cleanup(media_dev);
> 
> The calling function will do this cleanup when this function fails, so
> no need to do it here; just return ret.
> 

Ditto.


> > +
> > +	return ret;
> > +}
> > +
> > +int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev)
> > +{
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	unsigned int num_nodes = cam_dev->dev_node_num;
> > +	/* Total pad numbers is video devices + one seninf pad */
> > +	unsigned int num_subdev_pads = MTK_CAM_DEV_NODES + 1;
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	ret = mtk_cam_media_register(dev,
> > +				     &cam_dev->media_dev);
> > +	if (ret) {
> > +		dev_err(dev, "failed to register media device:%d\n", ret);
> > +		goto fail_media_dev;
> > +	}
> > +
> > +	ret = mtk_cam_v4l2_register(dev,
> > +				    &cam_dev->media_dev,
> > +				    &cam_dev->v4l2_dev,
> > +				    NULL);
> > +	if (ret) {
> > +		dev_err(dev, "failed to register V4L2 device:%d\n", ret);
> > +		goto fail_v4l2_dev;
> > +	}
> > +
> > +	/* Initialize subdev media entity */
> > +	cam_dev->subdev_pads = devm_kcalloc(dev, num_subdev_pads,
> > +					    sizeof(*cam_dev->subdev_pads),
> > +					    GFP_KERNEL);
> 
> It doesn't look like this gets free'd in any of the failure cases below.
> 

Based on Tomasz comments, we could use devm_kcalloc() here and remove
any matching calls to kfree(), since that would be cleaned up when
removing the driver.
https://patchwork.kernel.org/patch/10905223/
Do I have misunderstanding?

> > +	if (!cam_dev->subdev_pads) {
> > +		ret = -ENOMEM;
> > +		goto fail_subdev_pads;
> > +	}
> > +
> > +	ret = media_entity_pads_init(&cam_dev->subdev.entity,
> > +				     num_subdev_pads,
> > +				     cam_dev->subdev_pads);
> > +	if (ret) {
> > +		dev_err(dev, "failed initialize media pads:%d:\n", ret);
> > +		goto fail_subdev_pads;
> > +	}
> > +
> > +	/* Initialize all pads with MEDIA_PAD_FL_SOURCE */
> > +	for (i = 0; i < num_subdev_pads; i++)
> > +		cam_dev->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE;
> > +
> > +	/* Customize the last one pad as CIO sink pad. */
> > +	cam_dev->subdev_pads[MTK_CAM_DEV_NODES].flags = MEDIA_PAD_FL_SINK;
> > +
> > +	/* Initialize subdev */
> > +	v4l2_subdev_init(&cam_dev->subdev, &mtk_cam_subdev_ops);
> > +	cam_dev->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
> > +	cam_dev->subdev.entity.ops = &mtk_cam_media_ops;
> > +	cam_dev->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
> > +				V4L2_SUBDEV_FL_HAS_EVENTS;
> > +	snprintf(cam_dev->subdev.name, sizeof(cam_dev->subdev.name),
> > +		 "%s", MTK_CAM_DEV_P1_NAME);
> > +	v4l2_set_subdevdata(&cam_dev->subdev, cam_dev);
> > +	cam_dev->subdev.internal_ops = &mtk_cam_subdev_internal_ops;
> > +
> > +	dev_info(dev, "register subdev: %s\n", cam_dev->subdev.name);
> > +	ret = v4l2_device_register_subdev(&cam_dev->v4l2_dev, &cam_dev->subdev);
> > +	if (ret) {
> > +		dev_err(dev, "failed initialize subdev:%d\n", ret);
> > +		goto fail_subdev;
> > +	}
> > +
> > +	/* Create video nodes and links */
> > +	for (i = 0; i < num_nodes; i++) {
> 
> Consider moving some of this loop to a new function. This would simplify
> the failure handling below by by removing the fail_vdev and
> fail_vdev_media_entity labels, whose cleanup could move into the helper
> function. It would also break up this large function a bit.
> 

Ok, thanks for your suggestion.
We will add new mtk_cam_video_register_device function to register video
device for each nodes and simplify the error handling.

	/* Create video nodes and links */
	for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) {
		ret = mtk_cam_video_register_device(cam_dev, i);
		if (ret)
			goto fail_video_register;
	}

fail_video_register:
	i--;
	for (; i >= 0; i--) {
	video_unregister_device(&cam_dev->mem2mem2_nodes[i].vdev);
	media_entity_cleanup(&cam_dev->mem2mem2_nodes[i].vdev.entity);
	mutex_destroy(&cam_dev->mem2mem2_nodes[i].lock);
	}

> > +		struct mtk_cam_video_device *node = &cam_dev->mem2mem2_nodes[i];
> > +		struct video_device *vdev = &node->vdev;
> > +		struct vb2_queue *vbq = &node->vbq;
> > +		u32 output = !cam_dev->mem2mem2_nodes[i].desc.capture;
> > +		u32 link_flags = cam_dev->mem2mem2_nodes[i].desc.link_flags;
> > +
> > +		cam_dev->subdev_pads[i].flags = output ?
> > +			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
> > +
> > +		/* Initialize miscellaneous variables */
> > +		mutex_init(&node->lock);
> > +		spin_lock_init(&node->slock);
> > +		INIT_LIST_HEAD(&node->pending_list);
> > +
> > +		/* Initialize formats to default values */
> > +		mtk_cam_node_to_v4l2(cam_dev, i, vdev, &node->vdev_fmt);
> > +
> > +		/* Initialize media entities */
> > +		ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
> > +		if (ret) {
> > +			dev_err(dev, "failed initialize media pad:%d\n", ret);
> > +			goto fail_vdev_media_entity;
> > +		}
> > +		node->enabled = false;
> > +		node->id = i;
> > +		node->vdev_pad.flags = cam_dev->subdev_pads[i].flags;
> > +		vdev->entity.ops = NULL;
> > +
> > +		/* Initialize vbq */
> > +		vbq->type = node->vdev_fmt.type;
> > +		if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT)
> > +			vbq->io_modes = VB2_MMAP;
> > +		else
> > +			vbq->io_modes = VB2_MMAP | VB2_DMABUF;
> > +		if (node->desc.smem_alloc)
> > +			vbq->bidirectional = 1;
> > +		if (vbq->type == V4L2_BUF_TYPE_META_CAPTURE)
> > +			vdev->entity.function =
> > +				MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
> > +		vbq->ops = &mtk_cam_vb2_ops;
> > +		vbq->mem_ops = &vb2_dma_contig_memops;
> > +		vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer);
> > +		vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> > +		vbq->min_buffers_needed = 0;	/* Can streamon w/o buffers */
> > +		/* Put the process hub sub device in the vb2 private data */
> > +		vbq->drv_priv = cam_dev;
> > +		vbq->lock = &node->lock;
> > +		vbq->supports_requests = true;
> > +
> > +		ret = vb2_queue_init(vbq);
> > +		if (ret) {
> > +			dev_err(dev, "failed to init. vb2 queue:%d\n", ret);
> > +			goto fail_vdev;
> > +		}
> > +
> > +		/* Initialize vdev */
> > +		snprintf(vdev->name, sizeof(vdev->name), "%s %s",
> > +			 MTK_CAM_DEV_P1_NAME, node->desc.name);
> > +		vdev->release = video_device_release_empty;
> > +		vdev->fops = &mtk_cam_v4l2_fops;
> > +		vdev->lock = &node->lock;
> > +		vdev->v4l2_dev = &cam_dev->v4l2_dev;
> > +		vdev->queue = &node->vbq;
> > +		vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
> > +		/* Enable private control for image video devices */
> > +		if (node->desc.image) {
> > +			mtk_cam_ctrl_init(cam_dev, &node->ctrl_handler);
> > +			vdev->ctrl_handler = &node->ctrl_handler;
> > +		}
> > +		video_set_drvdata(vdev, cam_dev);
> > +		dev_dbg(dev, "register vdev:%d:%s\n", i, vdev->name);
> > +
> > +		ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> > +		if (ret) {
> > +			dev_err(dev, "failed to register vde:%d\n", ret);
> > +			goto fail_vdev;
> > +		}
> > +
> > +		/* Create link between video node and the subdev pad */
> > +		if (output) {
> > +			ret = media_create_pad_link(&vdev->entity, 0,
> > +						    &cam_dev->subdev.entity,
> > +						    i, link_flags);
> > +		} else {
> > +			ret = media_create_pad_link(&cam_dev->subdev.entity,
> > +						    i, &vdev->entity, 0,
> > +						    link_flags);
> > +		}
> > +		if (ret)
> > +			goto fail_link;
> > +	}
> > +
> > +	vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
> > +
> > +	return 0;
> > +
> > +	for (; i >= 0; i--) {
> > +fail_link:
> > +		video_unregister_device(&cam_dev->mem2mem2_nodes[i].vdev);
> > +fail_vdev:
> > +		media_entity_cleanup(&cam_dev->mem2mem2_nodes[i].vdev.entity);
> > +fail_vdev_media_entity:
> > +		mutex_destroy(&cam_dev->mem2mem2_nodes[i].lock);
> > +	}
> > +fail_subdev:
> > +	media_entity_cleanup(&cam_dev->subdev.entity);
> > +fail_subdev_pads:
> > +	v4l2_device_unregister(&cam_dev->v4l2_dev);
> > +fail_v4l2_dev:
> > +fail_media_dev:
> >
> media_device_unregister and media_device_cleanup are called when
> mtk_cam_media_register fails, so only need to call these under the
> fail_v4l2_dev label.
> 

Will correct in next patch.

> > +	dev_err(dev, "fail_v4l2_dev mdev: 0x%pK:%d", &cam_dev->media_dev, ret);
> > +	media_device_unregister(&cam_dev->media_dev);
> > +	media_device_cleanup(&cam_dev->media_dev);
> > +
> > +	return ret;
> > +}
> > +
> > +int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev)
> > +{
> > +	unsigned int i;
> > +	struct mtk_cam_video_device *dev;
> > +
> > +	for (i = 0; i < cam_dev->dev_node_num; i++) {
> > +		dev = &cam_dev->mem2mem2_nodes[i];
> > +		video_unregister_device(&dev->vdev);
> > +		media_entity_cleanup(&dev->vdev.entity);
> > +		mutex_destroy(&dev->lock);
> > +		if (dev->desc.image)
> > +			v4l2_ctrl_handler_free(&dev->ctrl_handler);
> > +	}
> > +
> > +	vb2_dma_contig_clear_max_seg_size(&cam_dev->pdev->dev);
> > +	v4l2_device_unregister_subdev(&cam_dev->subdev);
> > +	media_entity_cleanup(&cam_dev->subdev.entity);
> > +	kfree(cam_dev->subdev_pads);
> > +	v4l2_device_unregister(&cam_dev->v4l2_dev);
> > +	media_device_unregister(&cam_dev->media_dev);
> > +	media_device_cleanup(&cam_dev->media_dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_cam_dev_complete(struct v4l2_async_notifier *notifier)
> > +{
> > +	struct mtk_cam_dev *cam_dev =
> > +		container_of(notifier, struct mtk_cam_dev, notifier);
> > +	struct device *dev = &cam_dev->pdev->dev;
> > +	int ret;
> > +
> > +	ret = media_create_pad_link(&cam_dev->seninf->entity,
> > +				    MTK_CAM_SENINF_PAD_SRC,
> > +				    &cam_dev->subdev.entity,
> > +				    MTK_CAM_P1_HUB_PAD_SINK,
> > +				    0);
> > +	if (ret)
> 
> should this function return an error here?
> 

Ok, we will print error log and just return here.

> > +		dev_err(dev, "fail to create pad link %s %s err:%d\n",
> > +			cam_dev->seninf->entity.name,
> > +			cam_dev->subdev.entity.name,
> > +			ret);
> > +
> > +	dev_info(dev, "Complete the v4l2 registration\n");
> > +
> > +	ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev);
> > +	if (ret) {
> > +		dev_err(dev, "failed initialize subdev nodes:%d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier,
> > +				      struct v4l2_subdev *sd,
> > +				      struct v4l2_async_subdev *asd)
> > +{
> > +	struct mtk_cam_dev *cam_dev =
> > +		container_of(notifier, struct mtk_cam_dev, notifier);
> > +
> > +	cam_dev->seninf = sd;
> > +	dev_info(&cam_dev->pdev->dev, "%s is bounded\n", sd->entity.name);
> > +	return 0;
> > +}
> > +
> > +static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier,
> > +					struct v4l2_subdev *sd,
> > +					struct v4l2_async_subdev *asd)
> > +{
> > +	struct mtk_cam_dev *cam_dev =
> > +		container_of(notifier, struct mtk_cam_dev, notifier);
> 
> Should anything be done to cam_dev-seninf here, such as setting it to
> NULL?
> 

Ok, we will configure cam_dev-seninf to NULL here in next patch.

> > +
> > +	dev_dbg(&cam_dev->pdev->dev, "%s is unbounded\n", sd->entity.name);
> > +}
> > +
> > +static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier)
> > +{
> > +	return mtk_cam_dev_complete(notifier);
> > +}
> > +
> > +static const struct v4l2_async_notifier_operations mtk_cam_async_ops = {
> > +	.bound = mtk_cam_dev_notifier_bound,
> > +	.unbind = mtk_cam_dev_notifier_unbind,
> > +	.complete = mtk_cam_dev_notifier_complete,
> > +};
> > +
> > +static int mtk_cam_dev_fwnode_parse(struct device *dev,
> > +				    struct v4l2_fwnode_endpoint *vep,
> > +				    struct v4l2_async_subdev *asd)
> > +{
> > +	return 0;
> > +}
> > +
> > +int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev)
> > +{
> > +	int ret;
> > +
> > +	ret = v4l2_async_notifier_parse_fwnode_endpoints(
> > +		&cam_dev->pdev->dev, &cam_dev->notifier,
> > +		sizeof(struct v4l2_async_subdev),
> > +		mtk_cam_dev_fwnode_parse);
> 
> As far as I can tell, the fourth argument is optional, so I think you
> can just set NULL and remove mtk_cam_dev_fwnode_parse.
> 

Good point. Thanks for your suggestion.
Will fix in next patch.

> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	if (!cam_dev->notifier.num_subdevs)
> > +		return -ENODEV;
> > +
> > +	cam_dev->notifier.ops = &mtk_cam_async_ops;
> > +	dev_info(&cam_dev->pdev->dev, "mtk_cam v4l2_async_notifier_register\n");
> > +	ret = v4l2_async_notifier_register(&cam_dev->v4l2_dev,
> > +					   &cam_dev->notifier);
> > +	if (ret) {
> > +		dev_err(&cam_dev->pdev->dev,
> > +			"failed to register async notifier : %d\n", ret);
> > +		v4l2_async_notifier_cleanup(&cam_dev->notifier);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev)
> > +{
> > +	v4l2_async_notifier_unregister(&cam_dev->notifier);
> > +	v4l2_async_notifier_cleanup(&cam_dev->notifier);
> > +}
> > diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
> > new file mode 100644
> > index 000000000000..73b36916da08
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
> > @@ -0,0 +1,43 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018 MediaTek Inc.
> > + * Author: Frederic Chen <frederic.chen@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef __MTK_CAM_DEV_V4L2_H__
> > +#define __MTK_CAM_DEV_V4L2_H__
> > +
> > +#include <media/v4l2-device.h>
> > +#include <media/videobuf2-v4l2.h>
> > +
> > +int mtk_cam_videoc_querycap(struct file *file, void *fh,
> > +			    struct v4l2_capability *cap);
> > +int mtk_cam_enum_framesizes(struct file *filp, void *priv,
> > +			    struct v4l2_frmsizeenum *sizes);
> > +int mtk_cam_videoc_enum_fmt(struct file *file, void *fh,
> > +			    struct v4l2_fmtdesc *f);
> > +int mtk_cam_videoc_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
> > +int mtk_cam_videoc_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
> > +int mtk_cam_videoc_try_fmt(struct file *file,
> > +			   void *fh, struct v4l2_format *in_fmt);
> > +int mtk_cam_vidioc_enum_input(struct file *file, void *fh,
> > +			      struct v4l2_input *input);
> > +int mtk_cam_vidioc_g_input(struct file *file, void *fh, unsigned int *input);
> > +int mtk_cam_vidioc_s_input(struct file *file, void *fh, unsigned int input);
> > +int mtk_cam_meta_enum_format(struct file *file, void *fh,
> > +			     struct v4l2_fmtdesc *f);
> > +int mtk_cam_videoc_g_meta_fmt(struct file *file, void *fh,
> > +			      struct v4l2_format *f);
> > +int mtk_cam_vidioc_subscribe_event(struct v4l2_fh *fh,
> > +				   const struct v4l2_event_subscription *sub);
> > +
> > +#endif /* __MTK_CAM_DEV_V4L2_H__ */
> > -- 
> > 2.18.0
> > 

Best regards,


Jungo


_______________________________________________
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] 104+ messages in thread

* [RFC, V3 0/9] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver
       [not found] <jungo.lin@mediatek.com>
  2019-03-28  9:56 ` [RFC V1 00/12] meida: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
@ 2019-06-11  3:53 ` Jungo Lin
  2019-06-11  3:53   ` [RFC,v3 1/9] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
                     ` (8 more replies)
  1 sibling, 9 replies; 104+ messages in thread
From: Jungo Lin @ 2019-06-11  3:53 UTC (permalink / raw)
  To: tfiga, hverkuil, laurent.pinchart, matthias.bgg, mchehab
  Cc: devicetree, sean.cheng, rynn.wu, srv_heupstream, robh, ryan.yu,
	frankie.chiu, jungo.lin, sj.huang, linux-mediatek, ddavenport,
	frederic.chen, linux-arm-kernel, linux-media

Hello,

This RFC patch series adding the driver for Pass 1 (P1) unit in
Mediatek's camera ISP system on mt8183 SoC, which
will be used in camera features of CrOS. It's the first time Mediatek
develops ISP kernel drivers based on V4L2 and media controller
framework. I posted the main part of the ISP Pass 1 driver as RFC to
discuss first and would like some review comments on the overall
architecture of the driver.

Pass 1 unit processes image signal from sensor devices and accepts the
tuning parameters to adjust the image quality. It performs optical
black correction, defect pixel correction, W/IR imbalance correction
and lens shading correction for RAW processing.

The driver is implemented with V4L2 and media controller framework so
we have the following entities to describe the ISP pass 1 path.

(The current metadata interface used in meta input and partial meta
nodes is only a temporary solution to kick off the driver development
and is not ready to be reviewed yet.)

1. meta input (output video device): connect to ISP P1 sub device.
It accepts the tuning buffer from user.

2. ISP P1 (sub device): connect to partial meta 0/1/2/3,
main stream and packed out video devices. When processing an image,
Pass 1 hardware supports multiple output images with different sizes
and formats so it needs two capture video devices ("main stream" and
"packed out") to return the image data to the user.

3. main stream (capture video device): return the processed image data
which is used in capture scenario.

4. packed out (capture video device): return the processed image data
which is used in preview scenario.

5. partial meta 0 (capture video device): return the AE/AWB statistics.

6. partial meta 1 (capture video device): return the AF statistics.

7. partial meta 2 (capture video device): return the local contrast
   enhanced statistics.

8. partial meta 3 (capture video device): return the local motion
   vector statistics.

The overall patches of the series is:

* Patch 1 & 2 are dt-bindings & dts information related to ISP P1 driver.
* Patch 3 is Kconfig configuration for ISP P1 driver.
* Patch 4 extends the original V4L2 image & meta formats.
* Patch 5 add Mediatek specific v4l2 control ID for ISP P1 driver.
* Patch 6 provides V4L2 utility functions & default video devices
  configuration & initialization for ISP P1 driver.
* Patch 7 is the heart of ISP P1 driver. It handles the ISP
  HW configuration, provides interrupt handling and initializes
  the V4L2 device nodes and other functions.
* Patch 8 adds communication with the co-processor on the SoC through
  the SCP driver[2].
* Patch 9 provides ISP P1 shard memory management between ISP P1 &
  co-processor. It is controlled by child device of ISP P1 driver.

Here is ISP P1 media topology:
It is included the main/sub sensor & sen-inf sub-devices which are
implemented in below patch[1][4][5]:


/usr/bin/media-ctl -p -d /dev/media1

Media controller API version 4.19.43

Media device information
------------------------
driver          mtk-cam
model           MTK-ISP-P1-V4L2
serial
bus info        platform:1a000000.camisp
hw revision     0x0
driver version  4.19.43

Device topology
- entity 1: MTK-ISP-P1-V4L2 (12 pads, 8 links)
            type V4L2 subdev subtype Unknown flags 0
            device node name /dev/v4l-subdev5
    pad0: Sink
        <- "MK-ISP-P1-V4L2 meta input":0 []
    pad1: Source
        -> "MTK-ISP-P1-V4L2 main stream":0 [ENABLED]
    pad2: Source
        -> "MTK-ISP-P1-V4L2 packed out":0 [ENABLED]
    pad3: Source
        -> "MTK-ISP-P1-V4L2 partial meta 0":0 []
    pad4: Source
        -> "MTK-ISP-P1-V4L2 partial meta 1":0 []
    pad5: Source
        -> "MTK-ISP-P1-V4L2 partial meta 2":0 []
    pad6: Source
        -> "MTK-ISP-P1-V4L2 partial meta 3":0 []
    pad7: Source
    pad8: Source
    pad9: Source
    pad10: Source
    pad11: Sink
        <- "1a040000.seninf.mipi-csi":4 []

- entity 14: MTK-ISP-P1-V4L2 meta input (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video12
    pad0: Sink
        -> "MTK-ISP-P1-V4L2":0 []

- entity 20: MTK-ISP-P1-V4L2 main stream (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video13
    pad0: Source
        <- "MTK-ISP-P1-V4L2":1 [ENABLED]

- entity 26: MTK-ISP-P1-V4L2 packed out (1 pad, 1 link)
             type Node subtype V4L flags 0
            device node name /dev/video14
    pad0: Source
        <- "MTK-ISP-P1-V4L2":2 [ENABLED]

- entity 32: MTK-ISP-P1-V4L2 partial meta 0 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video15
    pad0: Source
        <- "MTK-ISP-P1-V4L2":3 []

- entity 38: MTK-ISP-P1-V4L2 partial meta 1 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video16
    pad0: Source
        <- "MTK-ISP-P1-V4L2":4 []

- entity 44: MTK-ISP-P1-V4L2 partial meta 2 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video17
    pad0: Source
        <- "MTK-ISP-P1-V4L2":5 []

- entity 50: MTK-ISP-P1-V4L2 partial meta 3 (1 pad, 1 link)
             type Node subtype V4L flags 0
             device node name /dev/video18
    pad0: Source
        <- "MTK-ISP-P1-V4L2":6 []

- entity 56: 1a040000.seninf.mipi-csi (12 pads, 3 links)
             type V4L2 subdev subtype Unknown flags 0
             device node name /dev/v4l-subdev6
    pad0: Sink
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
        <- "ov5695 2-0036":0 []
    pad1: Sink
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
        <- "ov2685 4-003c":0 []
    pad2: Sink
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad3: Sink
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad4: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
        -> "MTK-ISP-P1-V4L2":11 []
    pad5: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad6: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad7: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad8: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad9: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad10: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
    pad11: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]

- entity 69: ov5695 2-0036 (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev7
    pad0: Source
        [fmt:SBGGR10_1X10/2592x1944 field:none colorspace:srgb]
        -> "1a040000.seninf.mipi-csi":0 []

- entity 73: ov2685 4-003c (1 pad, 1 link)
             type V4L2 subdev subtype Sensor flags 0
             device node name /dev/v4l-subdev8
    pad0: Source
        [fmt:SBGGR10_1X10/1600x1200 field:none colorspace:srgb]
        -> "1a040000.seninf.mipi-csi":1 []

===========
= history =
===========

version 3:
 - Remove ISP Pass 1 reserved memory device node and change to use SCP's
   reserved memory region. (Rob Herring)
 - Fix comments of ISP Pass 1 device node & dt-bindings document (Rob Herring)
 - Revise ISP Pass1 Kconfig
 - Add rst documents for Mediatek image formats (Hans Verkuil)
 - Fix kernel warning messages when running v4l2_compliance test
 - Move AFO buffer enqueue & de-queue from request API to non-request
 - mtk_cam-ctrl.h/mtk_cam-ctrl.c
   Revise Mediatek ISP Pass1 specific V4L2 control naming & file licence declaration (Hans Verkuil)
   Split GET_BIN_INFO control into two controls to get width & height in-dependently (Hans Verkuil)
 - mtk_cam-v4l2-util.h/mtk_cam-v4l2-util.c
   Merging mtk_cam-dev.c and mtk_cam-v4l2-util.c. (Drew Davenport)
   Remove the pix_mode argument in related functions and unreachable code. (Drew Davenport)
   Fix Drew's comments which are addressed in v2 patch
   Fix some Tomasz comments which are addressed in DIP's v1 patch[3]
 - mtk_cam-regs.h / mtk_cam.h / mtk_cam.c
   Fix Drew's comments which are addressed in v2 patch
   Fix some Tomasz comments which are addressed in DIP's v1 patch[3]
   Refactoring mtk_isp_config & mtk_isp_req_enqueue functions
 - mtk_cam-scp.h / mtk_cam-scp.c
   Move function declarations from mtk_cam.h to mtk_cam-scp.h (Drew Davenport)
   Fix some Tomasz comments which are addressed in DIP's v1 patch[3]
   Fix ISP de-initialize timing KE issue
 - mtk_cam-smem.h / mtk_cam-smem-dev.c
   Get the reserved shared memory via SCP driver (Tomasz Figa)

Todo:
 - Add rst documents for Mediatek meta formats
 - New meta buffer structure design & re-factoring

version 2:
 - Add 3A enhancement feature which includes:
   Separates 3A pipeline out of frame basis to improve
   AE/AWB (exposure and white balance) performance.
   Add 2 SCP sub-commands for 3A meta buffers.
 - Add new child device to manage P1 shared memory between P1 HW unit
   and co-processor.
 - Remove mediatek,cam_smem.txt & cam_smem dts node in mt8183.dtsi.
 - Revised document wording for dt-bindings documents & dts information.
 - Remove mtk_cam-ctx.h & mtk_cam-dev-ctx-core.c and move these
   source codes to mtk_cam-dev.h & mtk_cam-dev.c.
 - mtk_cam-dev.h / mtk_cam-dev.c
   Revised mtk_cam_video_device & mtk_cam_dev to remove unused structure fields
   or add comments.
   Revised buffer size for LMVO & LCSO.
   Fix pixel format utility function.
   Add vb2_dma_contig_set_max_seg_size to configure DMA max segment size.
 - mtk_cam-v4l2-util.c
   Refactoring V4L2 async mechanism with seninf driver only
   Refactoring CIO (Connection IO) implementation with active sensor
   Revised stream on function for 3A enhancement feature
   Add new V4L2 en-queue/de-queue utility functions for 3A enhancement feature
 - mtk_cam-regs.h / mtk_cam.h / mtk_cam.c
   Add meta buffer index register definitions
   Add meta DMA configuration function.
   Separate with frame-base and non-frame-base en-queue/de-queue functions
   Add isp_setup_scp_rproc function to get RPC handle
   Add mtk_cam_reserved_memory_init for shared memory management
 - mtk_cam-scp.h / mtk_cam-scp.c
   Add new meta strictures for 3A enhancement feature
   Add new IPI command utility function for 3A enhancement feature
   Enhance isp_composer_dma_sg_init function flow
   Shorten overall IPI command structure size
   Remove scp_state state checking
   Improve code readability
 - mtk_cam-smem.h / mtk_cam-smem-dev.c
   Add mtk_cam_alloc_smem_dev to allocate one new child device of ISP driver.
   Handling P1 driver 's reserved memory & allocate DMA buffers based on this
   memory region.

TODOs:
 - 3A enhancement feature bug fixing

version 1:
 - Revised driver sources based on Tomasz's comments including
   part1/2/3/4 in RFC V0 patch.
 - Remove DMA cache mechanism.
   Support two new video devices (LCSO/LMVO) for advance camera
   features.
 - Fixed v4l2-compliance test failure items.
 - Add private controls for Mediatek camera middle-ware.
 - Replace VPU driver's APIs with new SCP driver interface for
   co-processor communication.
 - Refactoring mtk_cam_scp.c to use ring-buffers mechanism for IPI
   commands RX handling.
 - Fix internal bugs.

TODOs:
 - Remove mtk_cam_smem_drv.c & mtk_cam_smem.h and implement DMA pool
   for shared memory management.
 - Revised file names.
 - Support non frame-sync AFO/AAO DMA buffers

version 0:
- Initial submission

==================
 Dependent patch
==================

Camera ISP P1 driver depends on seninf driver, SCP driver.
The patches are listed as following:

[1]. media: support Mediatek sensor interface driver
https://patchwork.kernel.org/cover/10979135/

[2]. Add support for mt8183 SCP
https://patchwork.kernel.org/cover/10972143/

[3]. [RFC,V1,6/6] platform: mtk-isp: Add Mediatek DIP driver
https://patchwork.kernel.org/patch/10905223/

[4]. WIP: media: ov5695: support ov5695 sensor in mt8183
https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/1614887

[5]. WIP: media: ov2685: support ov2685 sensor in mt8183
https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/1614885

==================
 Compliance test
==================

The v4l2-compliance is built with the below lastest patch.
https://git.linuxtv.org/v4l-utils.git/commit/utils/v4l2-compliance?id=2aff3ef768c42cfdbb31d143ee2286a6b46e9db0

Note 1.
This testing depends on the above sensors[4][5] patches.

Note 2.
Before passing streaming on testing, need to enable media links between
entity 1: MTK-ISP-P1-V4L2, entity 20: MTK-ISP-P1-V4L2 main stream
and entity 26: MTK-ISP-P1-V4L2 packed out.

/usr/bin/media-ctl -i -d /dev/media1
Enter a link to modify or enter to stop
1:1->20:0[1]
1:1->20:0[1]
Enter a link to modify or enter to stop
1:2->26:0[1]
1:2->26:0[1]
Enter a link to modify or enter to stop

v4l2-compliance test output:
/usr/bin/v4l2-compliance -m /dev/media1

v4l2-compliance SHA: not available, 32 bits

Compliance test for mtk-cam device /dev/media1:

Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43

Required ioctls:
    test MEDIA_IOC_DEVICE_INFO: OK

Allow for multiple opens:
    test second /dev/media1 open: OK
    test MEDIA_IOC_DEVICE_INFO: OK
    test for unlimited opens: OK

Media Controller ioctls:
    test MEDIA_IOC_G_TOPOLOGY: OK
    Entities: 11 Interfaces: 11 Pads: 33 Links: 21
    test MEDIA_IOC_ENUM_ENTITIES/LINKS: OK
    test MEDIA_IOC_SETUP_LINK: OK

Total for mtk-cam device /dev/media1: 7, Succeeded: 7, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video12:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    Capabilities     : 0x8c200000
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps      : 0x0c200000
        Streaming
        Extended Pix Format
Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000010
    Type             : V4L Video
Entity Info:
    ID               : 0x0000000e (14)
    Name             : MTK-ISP-P1-V4L2 meta input
    Function         : V4L2 I/O
    Pad 0x0100000f   : 0: Sink
      Link 0x02000012: to remote pad 0x1000002 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video12 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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video12: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video13:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    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      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000016
    Type             : V4L Video
Entity Info:
    ID               : 0x00000014 (20)
    Name             : MTK-ISP-P1-V4L2 main stream
    Function         : V4L2 I/O
    Pad 0x01000015   : 0: Source
      Link 0x02000018: from remote pad 0x1000003 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video13 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: 1 Private Controls: 3

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)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    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
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video13: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video14:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    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      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x0300001c
    Type             : V4L Video
Entity Info:
    ID               : 0x0000001a (26)
    Name             : MTK-ISP-P1-V4L2 packed out
    Function         : V4L2 I/O
    Pad 0x0100001b   : 0: Source
      Link 0x0200001e: from remote pad 0x1000004 of entity 'MTK-ISP-P1-V4L2': Data, Enabled

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video14 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: 1 Private Controls: 3

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)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    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
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video14: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video15:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    Capabilities     : 0x84a00000
        Metadata Capture
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps      : 0x04a00000
        Metadata Capture
        Streaming
        Extended Pix Format
Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000022
    Type             : V4L Video
Entity Info:
    ID               : 0x00000020 (32)
    Name             : MTK-ISP-P1-V4L2 partial meta 0
    Function         : V4L2 I/O
    Pad 0x01000021   : 0: Source
      Link 0x02000024: from remote pad 0x1000005 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video15 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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video15: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video16:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    Capabilities     : 0x84a00000
        Metadata Capture
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps      : 0x04a00000
        Metadata Capture
        Streaming
        Extended Pix Format
Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000028
    Type             : V4L Video
Entity Info:
    ID               : 0x00000026 (38)
    Name             : MTK-ISP-P1-V4L2 partial meta 1
    Function         : V4L2 I/O
    Pad 0x01000027   : 0: Source
      Link 0x0200002a: from remote pad 0x1000006 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video16 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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video16: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video17:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    Capabilities     : 0x84a00000
        Metadata Capture
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps      : 0x04a00000
        Metadata Capture
        Streaming
        Extended Pix Format
Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x0300002e
    Type             : V4L Video
Entity Info:
    ID               : 0x0000002c (44)
    Name             : MTK-ISP-P1-V4L2 partial meta 2
    Function         : V4L2 I/O
    Pad 0x0100002d   : 0: Source
      Link 0x02000030: from remote pad 0x1000007 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video17 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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video17: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for MTK-ISP-P1-V4L2 device /dev/video18:

Driver Info:
    Driver name      : MTK-ISP-P1-V4L2
    Card type        : MTK-ISP-P1-V4L2
    Bus info         : platform:1a000000.camisp
    Driver version   : 4.19.43
    Capabilities     : 0x84a00000
        Metadata Capture
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps      : 0x04a00000
        Metadata Capture
        Streaming
        Extended Pix Format
Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000034
    Type             : V4L Video
Entity Info:
    ID               : 0x00000032 (50)
    Name             : MTK-ISP-P1-V4L2 partial meta 3
    Function         : V4L2 I/O
    Pad 0x01000033   : 0: Source
      Link 0x02000036: from remote pad 0x1000008 of entity 'MTK-ISP-P1-V4L2': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second /dev/video18 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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK
    test VIDIOC_TRY_FMT: OK
    test VIDIOC_S_FMT: OK
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
    test VIDIOC_EXPBUF: OK
    test Requests: OK

Total for MTK-ISP-P1-V4L2 device /dev/video18: 45, Succeeded: 45, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev5:

Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x0300004f
    Type             : V4L Sub-Device
Entity Info:
    ID               : 0x00000001 (1)
    Name             : MTK-ISP-P1-V4L2
    Function         : Video Statistics
    Pad 0x01000002   : 0: Sink
      Link 0x02000012: from remote pad 0x100000f of entity 'MTK-ISP-P1-V4L2 meta input': Data
    Pad 0x01000003   : 1: Source
      Link 0x02000018: to remote pad 0x1000015 of entity 'MTK-ISP-P1-V4L2 main stream': Data, Enabled
    Pad 0x01000004   : 2: Source
      Link 0x0200001e: to remote pad 0x100001b of entity 'MTK-ISP-P1-V4L2 packed out': Data, Enabled
    Pad 0x01000005   : 3: Source
      Link 0x02000024: to remote pad 0x1000021 of entity 'MTK-ISP-P1-V4L2 partial meta 0': Data
    Pad 0x01000006   : 4: Source
      Link 0x0200002a: to remote pad 0x1000027 of entity 'MTK-ISP-P1-V4L2 partial meta 1': Data
    Pad 0x01000007   : 5: Source
      Link 0x02000030: to remote pad 0x100002d of entity 'MTK-ISP-P1-V4L2 partial meta 2': Data
    Pad 0x01000008   : 6: Source
      Link 0x02000036: to remote pad 0x1000033 of entity 'MTK-ISP-P1-V4L2 partial meta 3': Data
    Pad 0x01000009   : 7: Source
    Pad 0x0100000a   : 8: Source
    Pad 0x0100000b   : 9: Source
    Pad 0x0100000c   : 10: Source
    Pad 0x0100000d   : 11: Sink
      Link 0x0200004d: from remote pad 0x100003d of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
    test second /dev/v4l-subdev5 open: OK
    test for unlimited opens: OK

Debug ioctls:
    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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 1):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 2):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 3):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 11):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
    test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
    test VIDIOC_QUERYCTRL: OK (Not Supported)
    test VIDIOC_G/S_CTRL: OK (Not Supported)
    test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
    test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
    test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
    Standard Controls: 0 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK (Not Supported)
    test VIDIOC_TRY_FMT: OK (Not Supported)
    test VIDIOC_S_FMT: OK (Not Supported)
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
    test VIDIOC_EXPBUF: OK (Not Supported)
    test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev5: 125, Succeeded: 125, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev6:

Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000051
    Type             : V4L Sub-Device
Entity Info:
    ID               : 0x00000038 (56)
    Name             : 1a040000.seninf.mipi-csi
    Function         : Video Interface Bridge
    Pad 0x01000039   : 0: Sink
      Link 0x02000047: from remote pad 0x1000046 of entity 'ov5695 2-0036': Data
    Pad 0x0100003a   : 1: Sink
      Link 0x0200004b: from remote pad 0x100004a of entity 'ov2685 4-003c': Data
    Pad 0x0100003b   : 2: Sink
    Pad 0x0100003c   : 3: Sink
    Pad 0x0100003d   : 4: Source
      Link 0x0200004d: to remote pad 0x100000d of entity 'MTK-ISP-P1-V4L2': Data
    Pad 0x0100003e   : 5: Source
    Pad 0x0100003f   : 6: Source
    Pad 0x01000040   : 7: Source
    Pad 0x01000041   : 8: Source
    Pad 0x01000042   : 9: Source
    Pad 0x01000043   : 10: Source
    Pad 0x01000044   : 11: Source

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
    test second /dev/v4l-subdev6 open: OK
    test for unlimited opens: OK

Debug ioctls:
    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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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)

Sub-Device ioctls (Sink Pad 0):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 1):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 2):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Sink Pad 3):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 4):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 5):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 6):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 7):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 8):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 9):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 10):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Sub-Device ioctls (Source Pad 11):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK (Not Supported)
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
    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: 2 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK (Not Supported)
    test VIDIOC_TRY_FMT: OK (Not Supported)
    test VIDIOC_S_FMT: OK (Not Supported)
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
    test VIDIOC_EXPBUF: OK (Not Supported)
    test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev6: 125, Succeeded: 125, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev7:

Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000053
    Type             : V4L Sub-Device
Entity Info:
    ID               : 0x00000045 (69)
    Name             : ov5695 2-0036
    Function         : Camera Sensor
    Pad 0x01000046   : 0: Source
      Link 0x02000047: to remote pad 0x1000039 of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
    test second /dev/v4l-subdev7 open: OK
    test for unlimited opens: OK

Debug ioctls:
    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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
    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: 11 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK (Not Supported)
    test VIDIOC_TRY_FMT: OK (Not Supported)
    test VIDIOC_S_FMT: OK (Not Supported)
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
    test VIDIOC_EXPBUF: OK (Not Supported)
    test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev7: 48, Succeeded: 48, Failed: 0, Warnings: 0
--------------------------------------------------------------------------------
Compliance test for mtk-cam device /dev/v4l-subdev8:

Media Driver Info:
    Driver name      : mtk-cam
    Model            : MTK-ISP-P1-V4L2
    Serial           : 
    Bus info         : platform:1a000000.camisp
    Media version    : 4.19.43
    Hardware revision: 0x00000000 (0)
    Driver version   : 4.19.43
Interface Info:
    ID               : 0x03000055
    Type             : V4L Sub-Device
Entity Info:
    ID               : 0x00000049 (73)
    Name             : ov2685 4-003c
    Function         : Camera Sensor
    Pad 0x0100004a   : 0: Source
      Link 0x0200004b: to remote pad 0x100003a of entity '1a040000.seninf.mipi-csi': Data

Required ioctls:
    test MC information (see 'Media Driver Info' above): OK

Allow for multiple opens:
    test second /dev/v4l-subdev8 open: OK
    test for unlimited opens: OK

Debug ioctls:
    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 (Not Supported)
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 0 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)

Sub-Device ioctls (Source Pad 0):
    test Try VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
    test Try VIDIOC_SUBDEV_G/S_FMT: OK
    test Try VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test Active VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: OK
    test Active VIDIOC_SUBDEV_G/S_FMT: OK
    test Active VIDIOC_SUBDEV_G/S_SELECTION/CROP: OK (Not Supported)
    test VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: OK (Not Supported)

Control ioctls:
    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: 10 Private Controls: 0

Format ioctls:
    test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
    test VIDIOC_G/S_PARM: OK (Not Supported)
    test VIDIOC_G_FBUF: OK (Not Supported)
    test VIDIOC_G_FMT: OK (Not Supported)
    test VIDIOC_TRY_FMT: OK (Not Supported)
    test VIDIOC_S_FMT: OK (Not Supported)
    test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
    test Cropping: OK (Not Supported)
    test Composing: OK (Not Supported)
    test Scaling: OK (Not Supported)

Codec ioctls:
    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:
    test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
    test VIDIOC_EXPBUF: OK (Not Supported)
    test Requests: OK (Not Supported)

Total for mtk-cam device /dev/v4l-subdev8: 48, Succeeded: 48, Failed: 0, Warnings: 0

Grand Total for mtk-cam device /dev/media1: 668, Succeeded: 668, Failed: 0, Warnings: 0

--------------------------------------------------------------------------------

Jungo Lin (9):
  dt-bindings: mt8183: Added camera ISP Pass 1
  dts: arm64: mt8183: Add ISP Pass 1 nodes
  media: platform: Add Mediatek ISP Pass 1 driver Kconfig
  media: platform: Add Mediatek ISP P1 image & meta formats
  media: platform: Add Mediatek ISP P1 V4L2 control
  media: platform: Add Mediatek ISP P1 V4L2 functions
  media: platform: Add Mediatek ISP P1 device driver
  media: platform: Add Mediatek ISP P1 SCP communication
  media: platform: Add Mediatek ISP P1 shared memory device

 .../bindings/media/mediatek,camisp.txt        |   57 +
 Documentation/media/uapi/v4l/pixfmt-mtb8.rst  |   49 +
 Documentation/media/uapi/v4l/pixfmt-mtba.rst  |   62 +
 Documentation/media/uapi/v4l/pixfmt-mtbc.rst  |   58 +
 Documentation/media/uapi/v4l/pixfmt-mtbe.rst  |   70 +
 Documentation/media/uapi/v4l/pixfmt-mtf8.rst  |   75 +
 Documentation/media/uapi/v4l/pixfmt-mtfa.rst  |   87 +
 Documentation/media/uapi/v4l/pixfmt-mtfc.rst  |  107 ++
 Documentation/media/uapi/v4l/pixfmt-mtfe.rst  |  107 ++
 arch/arm64/boot/dts/mediatek/mt8183.dtsi      |   24 +
 drivers/media/platform/Kconfig                |    2 +
 drivers/media/platform/mtk-isp/Kconfig        |   17 +
 drivers/media/platform/mtk-isp/Makefile       |    3 +
 .../media/platform/mtk-isp/isp_50/Makefile    |    5 +
 .../platform/mtk-isp/isp_50/cam/Makefile      |    9 +
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c         |  138 ++
 .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h         |   38 +
 .../mtk-isp/isp_50/cam/mtk_cam-regs.h         |  126 ++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c |  371 ++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h |  207 ++
 .../mtk-isp/isp_50/cam/mtk_cam-smem.c         |  304 +++
 .../mtk-isp/isp_50/cam/mtk_cam-smem.h         |   18 +
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c    | 1674 +++++++++++++++++
 .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h    |  173 ++
 .../platform/mtk-isp/isp_50/cam/mtk_cam.c     | 1087 +++++++++++
 .../platform/mtk-isp/isp_50/cam/mtk_cam.h     |  251 +++
 drivers/media/v4l2-core/v4l2-ioctl.c          |   13 +
 include/uapi/linux/v4l2-controls.h            |    4 +
 include/uapi/linux/videodev2.h                |   17 +
 29 files changed, 5153 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtb8.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtba.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtbc.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtbe.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtf8.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfa.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfc.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfe.rst
 create mode 100644 drivers/media/platform/mtk-isp/Kconfig
 create mode 100644 drivers/media/platform/mtk-isp/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
 create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h

-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,v3 1/9] dt-bindings: mt8183: Added camera ISP Pass 1
  2019-06-11  3:53 ` [RFC, V3 0/9] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
@ 2019-06-11  3:53   ` Jungo Lin
  2019-06-11  3:53   ` [RFC,v3 2/9] dts: arm64: mt8183: Add ISP Pass 1 nodes Jungo Lin
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-06-11  3:53 UTC (permalink / raw)
  To: tfiga, hverkuil, laurent.pinchart, matthias.bgg, mchehab
  Cc: devicetree, sean.cheng, rynn.wu, srv_heupstream, robh, ryan.yu,
	frankie.chiu, jungo.lin, sj.huang, linux-mediatek, ddavenport,
	frederic.chen, linux-arm-kernel, linux-media

This patch adds DT binding document for the Pass 1 (P1) unit in
Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data
out from the sensor interface, applies ISP image effects from tuning
data and outputs the image data or statistics data to DRAM.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 .../bindings/media/mediatek,camisp.txt        | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt

diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
new file mode 100644
index 000000000000..50a8b4d9ac8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt
@@ -0,0 +1,57 @@
+* Mediatek Image Signal Processor Pass 1 (ISP P1)
+
+The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out
+from the sensor interface, applies ISP effects from tuning data and outputs
+the image data and statistics data to DRAM. Furthermore, Pass 1 unit has
+the ability to output two different resolutions frames at the same time to
+increase the performance of the camera application.
+
+Required properties:
+- compatible: Must be "mediatek,mt8183-camisp" for MT8183.
+- reg: Physical base address of the camera function block registers and
+  length of memory mapped region. Must contain an entry for each entry
+  in reg-names.
+- reg-names: Must include the following entries:
+  "cam_sys": Camsys base function block
+  "cam_uni": Camera UNI function block
+  "cam_a": Single camera ISP P1 hardware module A
+  "cam_b": Single camera ISP P1 hardware module B
+- interrupts: Interrupt number to the CPU.
+- iommus: Shall point to the respective IOMMU block with master port
+  as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- clocks: A list of phandle and clock specifier pairs as listed
+  in clock-names property, see
+  Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- clock-names: Must be "camsys_cam_cgpdn" and "camsys_camtg_cgpdn".
+- mediatek,larb: Must contain the local arbiters in the current SoCs, see
+  Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+  for details.
+- mediatek,scp : The node of system control processor (SCP), see
+  Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details.
+
+Example:
+SoC specific DT entry:
+
+		camisp: camisp@1a000000 {
+			compatible = "mediatek,mt8183-camisp", "syscon";
+			reg = <0 0x1a000000 0 0x1000>,
+			      <0 0x1a003000 0 0x1000>,
+			      <0 0x1a004000 0 0x2000>,
+			      <0 0x1a006000 0 0x2000>;
+			reg-names = "cam_sys",
+				    "cam_uni",
+				    "cam_a",
+				    "cam_b";
+			interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+			iommus = <&iommu M4U_PORT_CAM_IMGO>;
+			clocks = <&camsys CLK_CAM_CAM>,
+				 <&camsys CLK_CAM_CAMTG>;
+			clock-names = "camsys_cam_cgpdn",
+				      "camsys_camtg_cgpdn";
+			mediatek,larb = <&larb3>,
+					<&larb6>;
+			mediatek,scp = <&scp>;
+		};
\ No newline at end of file
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,v3 2/9] dts: arm64: mt8183: Add ISP Pass 1 nodes
  2019-06-11  3:53 ` [RFC, V3 0/9] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  2019-06-11  3:53   ` [RFC,v3 1/9] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
@ 2019-06-11  3:53   ` Jungo Lin
  2019-06-11  3:53   ` [RFC,v3 3/9] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Jungo Lin
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 104+ messages in thread
From: Jungo Lin @ 2019-06-11  3:53 UTC (permalink / raw)
  To: tfiga, hverkuil, laurent.pinchart, matthias.bgg, mchehab
  Cc: devicetree, sean.cheng, rynn.wu, srv_heupstream, robh, ryan.yu,
	frankie.chiu, jungo.lin, sj.huang, linux-mediatek, ddavenport,
	frederic.chen, linux-arm-kernel, linux-media

Add nodes for Pass 1 unit of Mediatek's camera ISP system.
Pass 1 unit embedded in Mediatek SoCs, works with the
co-processor to process image signal from the image sensor
and output RAW image data.

Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183.dtsi | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index 75c4881bbe5e..8a725357c594 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -369,5 +369,29 @@
 			reg = <0 0x1a000000 0 0x1000>;
 			#clock-cells = <1>;
 		};
+
+		camisp: camisp@1a000000 {
+			compatible = "mediatek,mt8183-camisp", "syscon";
+			reg = <0 0x1a000000 0 0x1000>,
+			      <0 0x1a003000 0 0x1000>,
+			      <0 0x1a004000 0 0x2000>,
+			      <0 0x1a006000 0 0x2000>;
+			reg-names = "cam_sys",
+				    "cam_uni",
+				    "cam_a",
+				    "cam_b";
+			interrupts = <GIC_SPI 253 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 254 IRQ_TYPE_LEVEL_LOW>,
+				     <GIC_SPI 255 IRQ_TYPE_LEVEL_LOW>;
+			iommus = <&iommu M4U_PORT_CAM_IMGO>;
+			clocks = <&camsys CLK_CAM_CAM>,
+				 <&camsys CLK_CAM_CAMTG>;
+			clock-names = "camsys_cam_cgpdn",
+				      "camsys_camtg_cgpdn";
+			mediatek,larb = <&larb3>,
+					<&larb6>;
+			mediatek,scp = <&scp>;
+		};
+
 	};
 };
-- 
2.18.0


_______________________________________________
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] 104+ messages in thread

* [RFC,v3 3/9] media: platform: Add Mediatek ISP Pass 1 driver Kconfig
  2019-06-11  3:53 ` [RFC, V3 0/9] media: platform: mtk-isp: Add Mediatek ISP Pass 1 driver Jungo Lin
  2019-06-11  3:53   ` [RFC,v3 1/9] dt-bindings: mt8183: Added camera ISP Pass 1 Jungo Lin
  2019-06-11  3:53   ` <